Project

General

Profile

1
<?php
2

    
3
/**
4
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19

    
20
/**
21
 * UpgradeHelper.php
22
 *
23
 * @category     Core
24
 * @package      Core_Upgrade
25
 * @copyright    Werner v.d.Decken <wkl@isteam.de>
26
 * @author       Werner v.d.Decken <wkl@isteam.de>
27
 * @license      http://www.gnu.org/licenses/gpl.html   GPL License
28
 * @version      0.0.1
29
 * @revision     $Revision: 2113 $
30
 * @link         $HeadURL: svn://isteam.dynxs.de/wb-archiv/branches/2.8.x/wb/framework/UpgradeHelper.php $
31
 * @lastmodified $Date: 2014-11-30 23:50:27 +0100 (Sun, 30 Nov 2014) $
32
 * @since        File available since 17.03.2013
33
 * @description  some helper function for upgrade-script.php
34
 */
35
class UpgradeHelper {
36

    
37
/**
38
 * do not delete start directory of deltree
39
 */
40
	const DEL_ROOT_PRESERVE = 0;
41
/**
42
 * delete start directory of deltree
43
 */
44
	const DEL_ROOT_DELETE   = 1;
45
/**
46
 * clear logs
47
 */
48
	const LOG_CLEAR = true;
49
/**
50
 * preserve logs
51
 */
52
	const LOG_PRESERVE = false;
53
/**
54
 * to store the last delTree log
55
 */
56
	static $aDelTreeLog = array();
57

    
58
/**
59
 * Compare available tables against a list of tables
60
 * @param  array list of needed table names without TablePrefix
61
 * @return array list of missing tables
62
 * @description this method is the replaement for self::existsAllTables()
63
 */
64
	public static function getMissingTables(array $aTablesList)
65
	{
66
		$aTablesList = array_flip($aTablesList);
67
		$oDb = WbDatabase::getInstance();
68
		$sPattern = addcslashes ( $oDb->TablePrefix, '%_' );
69
		if (($oTables = $oDb->query( 'SHOW TABLES LIKE "'.$sPattern.'%"'))) {
70
			while ($aTable = $oTables->fetchRow(MYSQL_NUM)) {
71
				$sTable =  preg_replace('/^'.preg_quote($oDb->TablePrefix, '/').'/s', '', $aTable[0]);
72
				if (isset($aTablesList[$sTable])) {
73
					unset($aTablesList[$sTable]);
74
				}
75
			}
76
		}
77
		return array_flip($aTablesList);
78
	}
79
/**
80
 * Alias for self::getMissingTables()
81
 * @param array list of needed table names without TablePrefix
82
 * @return array list of missing tables
83
 */
84
	public static function existsAllTables(array $aTablesList)
85
	{
86
		return self::getMissingTables($aTablesList);
87
	}
88
/**
89
 * Sanitize and repair Pagetree links
90
 * @return boolean|int number of updated records or false on error
91
 */
92
	public static function sanitizePagesTreeLinkStructure()
93
	{
94
		$oDb = WbDatabase::getInstance();
95
		$iCounter = 0;
96
		$aPages = array();
97
		try {
98
			$sql = 'SELECT `page_id`, `link`, `page_trail` '
99
				 . 'FROM `'.$oDb->TablePrefix.'pages` '
100
				 . 'ORDER BY `page_id`';
101
			$oPages = $oDb->doQuery($sql);
102
			// read 'page_id', 'link' and 'page_trail' from all pages
103
			while ($aPage = $oPages->fetchRow(MYSQL_ASSOC)) {
104
				// extact filename only from complete link
105
				$aPages[$aPage['page_id']]['filename'] = preg_replace('/.*?\/([^\/]*$)/', '\1', $aPage['link']);
106
				$aPages[$aPage['page_id']]['page_trail'] = $aPage['page_trail'];
107
				$aPages[$aPage['page_id']]['link'] = $aPage['link'];
108
			}
109
			foreach ($aPages as $iKey=>$aRecord) {
110
			// iterate all pages
111
				$aTmp = array();
112
				$aIds = explode(',', $aRecord['page_trail']);
113
				// rebuild link from filenames using page_trail
114
				foreach($aIds as $iId) {
115
					$aTmp[] = $aPages[$iId]['filename'];
116
				}
117
				$sLink = '/'.implode('/', $aTmp);
118
				if ($sLink != $aPages[$iKey]['link']) {
119
				// update page if old link is different to new generated link
120
					$sql = 'UPDATE `'.$oDb->TablePrefix.'pages` '
121
						 . 'SET `link`=\''.$sLink.'\' '
122
						 . 'WHERE `page_id`='.$iKey;
123
					$oDb->doQuery($sql);
124
					$iCounter++;
125
				}
126
			}
127
		} catch(WbDatabaseException $e) {
128
			return false;
129
		}
130
		return $iCounter;
131
	}
132
/**
133
 *
134
 * @param string $sMsg Message to show
135
 */
136
	public static function dieWithMessage($sMsg)
137
	{
138
		$sMsg = 'Fatal error occured during initial startup.<br /><br />'.PHP_EOL.$sMsg
139
			  . '<br />'.PHP_EOL.'Please correct these errors and then '
140
			  . '<a href="http://'.$_SERVER["HTTP_HOST"].$_SERVER["SCRIPT_NAME"].'" '
141
			  . 'title="restart">klick here to restart the upgrade-script</a>.<br />'.PHP_EOL;
142
		die($sMsg);
143
	}
144
/**
145
 *
146
 * @param string $sAppPath path to the current installation
147
 * @return boolean
148
 */
149
	public static function checkSetupFiles($sAppPath)
150
	{
151
		if (defined('DB_PASSWORD')) {
152
		// old config.php is active
153
			if (    !is_writable($sAppPath.'config.php')
154
                 || (file_exists($sAppPath.'config.php.new') && !is_writable($sAppPath.'config.php.new'))
155
                 || (file_exists($sAppPath.'setup.ini.php') && !is_writable($sAppPath.'setup.ini.php'))
156
                 || (!file_exists($sAppPath.'setup.ini.php') && !is_writable($sAppPath.'setup.ini.php.new'))
157
               )
158
			{
159
			// stop script if there's an error occured
160
				$sMsg = 'Following files must exist and be writable to run upgrade:<br />'.PHP_EOL
161
				      . '<ul>'.PHP_EOL
162
                      . '<li>config.php</li>'.PHP_EOL
163
                      . (file_exists($sAppPath.'config.php.new') ? '<li>config.php.new</li>'.PHP_EOL : '')
164
                      . (file_exists($sAppPath.'setup.ini.php') ? '<li>setup.ini.php</li>'.PHP_EOL : '')
165
                      . (!file_exists($sAppPath.'setup.ini.php') ? '<li>setup.ini.php.new</li>'.PHP_EOL : '')
166
                      . '</ul>'.PHP_EOL;
167
				self::dieWithMessage($sMsg);
168
			} else { // ok, let's change the files now!
169
				if (file_exists($sAppPath.'setup.ini.php')) {
170
				// if 'setup.ini.php' exists
171
					if (!is_writeable($sAppPath.'setup.ini.php')) {
172
					// but it's not writable
173
						$sMsg = 'The file \'setup.ini.php\' already exists but is not writeable!';
174
						self::dieWithMessage($sMsg);
175
					}
176
				} else {
177
				// try to rename 'setup.ini.php.new' into 'setup.ini.php'
178
					if (!rename($sAppPath.'setup.ini.php.new', $sAppPath.'setup.ini.php')) {
179
						$sMsg = 'Can not rename \''.$sAppPath.'setup.ini.php.new\' into \''.$sAppPath.'setup.ini.php\' !!<br />'
180
						      . 'Create an empty file \''.$sAppPath.'setup.ini.php\' and make it writeable for the server!';
181
						self::dieWithMessage($sMsg);
182
					}
183
				}
184
			// now first read constants from old config.php
185
				$sContent = file_get_contents($sAppPath.'config.php');
186
				$sPattern = '/^\s*define\s*\(\s*([\'\"])(.*?)\1\s*,.*;\s*$/m';
187
				if (preg_match_all($sPattern, $sContent, $aMatches)) {
188
				// get all already defined consts
189
					$aAllConsts = get_defined_constants(true);
190
					$aSetupConsts = array();
191
				// collect the needed values from defined consts
192
					foreach($aMatches[2] as $sConstName) {
193
						$aSetupConsts[$sConstName] = (isset($aAllConsts['user'][$sConstName]) ? $aAllConsts['user'][$sConstName] : '');
194
					}
195
				// try to sanitize available values
196
					$aSetupConsts['DB_TYPE'] = ((isset($aSetupConsts['DB_TYPE']) && $aSetupConsts['DB_TYPE'] != '') ? $aSetupConsts['DB_TYPE'] : 'mysql');
197
					$aSetupConsts['DB_PORT'] = ((isset($aSetupConsts['DB_PORT']) && $aSetupConsts['DB_PORT'] != '') ? $aSetupConsts['DB_PORT'] : '3306');
198
					$aSetupConsts['DB_CHARSET'] = (isset($aSetupConsts['DB_CHARSET']) ? $aSetupConsts['DB_CHARSET'] : '');
199
					$aSetupConsts['DB_CHARSET'] = preg_replace('/[^0-9a-z]*/i', '', $aSetupConsts['DB_CHARSET']);
200
					$aSetupConsts['TABLE_PREFIX'] = (isset($aSetupConsts['TABLE_PREFIX']) ? $aSetupConsts['TABLE_PREFIX'] : '');
201
					$aSetupConsts['ADMIN_DIRECTORY'] = trim(str_replace('\\', '/', preg_replace('/^'.preg_quote(WB_PATH, '/').'/', '', ADMIN_PATH)), '/').'/';
202
					$aSetupConsts['WB_URL'] = rtrim(str_replace('\\', '/', WB_URL), '/').'/';
203
				// Try and write settings to config file
204
					if (self::writeSetupIni($sAppPath, $aSetupConsts)) {
205
						if (self::writeConfig($sAppPath)) {
206
							return true;
207
						} else {
208
							$sMsg ='Error writing \''.$sAppPath.'config.php\'!';
209
						}
210
					} else {
211
						$sMsg ='Error writing \''.$sAppPath.'setup.ini.php\'!';
212
					}
213
				} else {
214
					$sMsg = 'No valid content found in \''.$sAppPath.'config.php\'!';
215
				}
216
				self::dieWithMessage($sMsg);
217
			}
218
		} else {
219
			$sFileContent = file_get_contents($sAppPath.'config.php');
220
			if (preg_match('/\s*define\s*\(.*\)\s*;/i', $sFileContent)) {
221
			// if config.php does not contain any defines
222
				if (is_writable($sAppPath.'config.php')) {
223
				// overwrite config.php with default content for compatibility
224
					if (self::writeConfig($sAppPath.'config.php')) {
225
						return true;
226
					} else  {
227
						$sMsg ='Error writing \''.$sAppPath.'config.php\'!';
228
					}
229
				} else {
230
					$sMsg ='File is not writable \''.$sAppPath.'config.php\'!';
231
				}
232
				self::dieWithMessage($sMsg);
233
			}
234
		}
235
	}
236
/**
237
 *
238
 * @param string $sAppPath the path where setup.ini.php is located
239
 * @param array  $aSetupValues
240
 * @return boolean
241
 */
242
	public static function writeSetupIni($sAppPath, array $aSetupValues)
243
	{
244
		$sSetupContent =
245
			";<?php die('sorry, illegal file access'); ?>#####\n"
246
		   .";################################################\n"
247
		   ."; WebsiteBaker configuration file\n"
248
		   ."; auto generated ".date('Y-m-d h:i:s A e ')."\n"
249
		   .";################################################\n"
250
		   ."[Constants]\n"
251
		   ."DEBUG   = false\n"
252
		   ."AppUrl  = \"".$aSetupValues['WB_URL']."\"\n"
253
		   ."AcpDir  = \"".$aSetupValues['ADMIN_DIRECTORY']."\"\n"
254
		   .";##########\n"
255
		   ."[DataBase]\n"
256
		   ."type    = \"".$aSetupValues['DB_TYPE']."\"\n"
257
		   ."user    = \"".$aSetupValues['DB_USERNAME']."\"\n"
258
		   ."pass    = \"".$aSetupValues['DB_PASSWORD']."\"\n"
259
		   ."host    = \"".$aSetupValues['DB_HOST']."\"\n"
260
		   ."port    = \"".$aSetupValues['DB_PORT']."\"\n"
261
		   ."name    = \"".$aSetupValues['DB_NAME']."\"\n"
262
		   ."charset = \"".$aSetupValues['DB_CHARSET']."\"\n"
263
		   ."table_prefix = \"".$aSetupValues['TABLE_PREFIX']."\"\n"
264
		   .";\n"
265
		   .";################################################\n";
266
		$sSetupFile = $sAppPath.'setup.ini.php';
267
		if (file_put_contents($sSetupFile, $sSetupContent)) {
268
			return true;
269
		}
270
		return false;
271
	}
272
/**
273
 * 
274
 * @param string $sAppPath the path where config.php is located
275
 * @return boolean
276
 */
277
	public static function writeConfig($sAppPath)
278
	{
279
		$sConfigContent = "<?php\n"
280
			."/* this file is for backward compatibility only */\n"
281
			."/* never put any code in this file! */\n"
282
			."include_once(dirname(__FILE__).'/framework/initialize.php');\n";
283
		$sConfigFile = $sAppPath.'config.php';
284
		if (file_put_contents($sConfigFile, $sConfigContent)) {
285
			return true;
286
		}
287
		return false;
288
	}
289
/**
290
 * Delete all contents of basedir, but not the basedir itself
291
 * @param string  $sRootDir the content of which should be deleted
292
 * @param integer $iMode    the mode can be set to self::DEL_ROOT_PRESERVE(default) or self::DEL_ROOT_DELETE
293
 * @return boolean          false if a file or directory can't be deleted
294
 */
295
	static public function delTree($sRootDir, $iMode = self::DEL_ROOT_PRESERVE)
296
	{
297
		// check if root dir is inside the installation and is writeable
298
		$oReg = WbAdaptor::getInstance();
299
		self::$aDelTreeLog = array();
300
		$bResult = true;
301
		if (!is_writeable($sRootDir)) {
302
			self::$aDelTreeLog[] = str_replace($oReg->AppPath, '', $sRootDir);
303
			return false;
304
		}
305
		$oIterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($sRootDir), RecursiveIteratorIterator::CHILD_FIRST);
306
		foreach ($oIterator as $oPath) {
307
			$sPath = rtrim(str_replace('\\', '/', $oPath->__toString()), '/');
308
			if ($oPath->isDir() && !preg_match('/\.$/s', $sPath)) {
309
				// proceed directories
310
				if (!rmdir($sPath)) {
311
					$bResult = false;
312
					self::$aDelTreeLog[] = str_replace($oReg->AppPath, '', $sPath);
313
				}
314
			} elseif ($oPath->isFile()) {
315
				// proceed files
316
				if (!unlink($sPath)) {
317
					$bResult = false;
318
					self::$aDelTreeLog[] = str_replace($oReg->AppPath, '', $sPath);
319
				}
320
			}
321
		}
322
		if (($iMode & self::DEL_ROOT_DELETE) && $bResult) {
323
        // delete RootDir if there was no error before
324
            if (!rmdir($sRootDir)) {
325
                $bResult = false;
326
                self::$aDelTreeLog[] = str_replace($oReg->AppPath, '', $sRootDir);
327
            }
328
		}
329
		return $bResult;
330
	}
331
/**
332
 * returns the log of the last delTree request
333
 * @param  bool  $bClearLog   TRUE clears the log after request, FALSE preserve the log
334
 * @return array
335
 */
336
	static public function getDelTreeLog($bClearLog = self::LOG_CLEAR)
337
	{
338
		$aRetval = self::$aDelTreeLog;
339
		if($bClearLog) { self::$aDelTreeLog = array(); }
340
		return $aRetval;
341
	}
342

    
343

    
344
    static public function convInfoIni2InfoPhp($sIniDir)
345
    {
346
        $aVarTypePrefixes = array(
347
            'template' => 'template',
348
            'theme'    => 'template',
349
            'tool'     => 'module',
350
            'page'     => 'module',
351
            'snippet'  => 'module'
352
        );
353
        $aNeededVars = array_flip(
354
            array('directory','name','function','version','platform','author','license','license_terms','description')
355
        );
356
        $aRetval = array();
357
        $sIniDir = rtrim(str_replace('\\', '/', $sIniDir), '/').'/';
358
        if (is_readable($sIniDir.'info.ini')) {
359
            if (($ini = parse_ini_file($sIniDir.'info.ini', true))) {
360
                if (!array_key_exists($ini['info']['type'], $aVarTypePrefixes)) {
361
                    return null;
362
                }
363
                $aRetval['prefix'] = $aVarTypePrefixes[$ini['info']['type']];
364
                $aRetval['function'] = $ini['info']['type'];
365
                unset($aNeededVars['function']);
366
                foreach ($ini['info'] as $sVar => $sValue) {
367
                    $aRetval[$sVar] = $sValue;
368
                    unset($aNeededVars[$sVar]);
369
                }
370
                if (isset($ini['platform']['versions'])) {
371
                    $aRetval['platform'] = $ini['platform']['versions'];
372
                    unset($aNeededVars['platform']);
373
                }
374
                if  (sizeof($aNeededVars) > 0) {
375
                    return null;
376
                }
377
            } else {
378
                return null;
379
            }
380
        } else {
381
            return null;
382
        }
383
        return $aRetval;
384
    } // end of function Ini2PhpInfo
385

    
386
} // end of class UpgradeHelper
387

    
(16-16/37)