Project

General

Profile

1 1643 darkviper
<?php
2
/**
3
 *
4
 * @category        WBCore
5
 * @package         WBCore_addons
6
 * @author          Werner v.d.Decken
7
 * @copyright       Website Baker Org. e.V.
8
 * @link			http://www.websitebaker2.org/
9
 * @license         http://www.gnu.org/licenses/gpl.html
10
 * @revision        $Revision$
11
 * @filesource		$HeadURL$
12
 * @lastmodified    $Date$
13
 */
14
15
class CopyTheme{
16
17
// global vars
18
	private $_oDb             = null;
19
	private $_aLang           = array();
20
	private $_aGlobals        = array();
21
// class properties
22
	private $_sThemesBasePath = ''; // (/var/www/httpdocs/wb/templates)
23
	private $_sSourceDir      = ''; // (default_theme)
24
	private $_sNewThemeDir    = ''; // (MyNewBackendTheme)
25
	private $_sNewThemeName   = ''; // ('my new Backend Theme')
26
27
	private $_aErrors         = array();
28
29
/**
30
 *
31
 * @param string $sSourceThemePath (THEME_PATH.'/templates/'.DEFAULT_THEME)
32
 * @param string $sNewThemeName (NewName)
33
 */
34
	public function __construct()
35
	{
36
	// import global vars
37
		$this->_oDb   = $GLOBALS['database'];
38
		$this->_aLang = $GLOBALS['MESSAGE'];
39
	// import global Consts
40
		$this->_aGlobals['TablePrefix']     = TABLE_PREFIX;
41 1648 darkviper
		$this->_aGlobals['AddonTable']      = 'addons';
42
		$this->_aGlobals['SettingsTable']   = 'settings';
43
44 1643 darkviper
		$this->_aGlobals['Debug']           = (defined('DEBUG') && DEBUG === true);
45
		$this->_aGlobals['IsLinux']         = ((substr(__FILE__, 0, 1)) == '/');
46
		$this->_aGlobals['StringDirMode']   = STRING_DIR_MODE;
47
		$this->_aGlobals['StringFileMode']  = STRING_FILE_MODE;
48
	}
49
/**
50
 * start copy current theme into a new theme
51
 * @param string $sSourceThemePath (/var/www/httpdocs/wb/templates/destination_theme)
52
 * @param string $sNewThemeName (NewName)
53
 * @return bool
54
 */
55
	public function execute($sSourceThemePath, $sNewThemeName)
56
	{
57
		$bRetval = false;
58
		$this->clearError();
59
		$this->_init($sSourceThemePath, $sNewThemeName);
60
		if(!is_writeable($this->_sThemesBasePath)) {
61
			$this->_setError( $this->_aLang['THEME_DESTINATION_READONLY'].' >> '
62
			                  .$this->_sThemesBasePath );
63
			return false;
64
		}
65
		if($this->_copyTree()) {
66
			if($this->_modifyInfoFile()) {
67
				if($this->_registerNewTheme()) {
68 1648 darkviper
					if($this->_activateTheme()) {
69
						return true;
70
					}
71 1643 darkviper
				}
72
			}
73
		}
74
		$dir = $this->_sThemesBasePath.'/'.$this->_sNewThemeDir;
75
		$this->_doRollback($dir);
76
		return false;
77
	}
78
/**
79
 * initialize the class
80
 * @param string $sSourceThemePath (/var/www/httpdocs/wb/templates/destination_theme)
81
 * @param string $sNewThemeName (NewName)
82
 * @return boolean
83
 */
84
	private function _init($sSourceThemePath, $sNewThemeName)
85
	{
86
	// sanitize arguments and calculate local vars
87
		$this->_sThemesBasePath = str_replace('\\','/', dirname($sSourceThemePath));
88
		$this->_sSourceDir = basename($sSourceThemePath);
89
		$this->_sNewThemeDir = $this->_SanitizeNewDirName($sNewThemeName);
90
		$this->_sNewThemeName = $this->_SanitizeNewName($sNewThemeName);
91
	}
92
/**
93
 * sanitize the new name and make it unique
94
 * @param string $sName
95
 * @return string
96
 */
97
	private function _SanitizeNewName($sName)
98
	{
99 1644 darkviper
		$sName = (trim($sName) == '' ? 'MyNewTheme' : $sName);
100 1643 darkviper
		$sName = mysql_real_escape_string($sName);
101
		$iCount = '';
102
		do {
103
			$sSearch = $sName.($iCount ? ' '.$iCount : '');
104 1648 darkviper
			$sql = 'SELECT COUNT(*) '
105
			     . 'FROM `'.$this->_aGlobals['TablePrefix'].$this->_aGlobals['AddonTable'].'` '
106 1643 darkviper
				 . 'WHERE LOWER(`name`)=\''.strtolower($sSearch).'\' AND `function`=\'theme\'';
107
			$exists = $this->_oDb->get_one($sql);
108
			$iCount = (int)$iCount + 1;
109
		}while($exists);
110
		return ($sName != $sSearch ? $sSearch : $sName);
111
	}
112
/**
113
 * generate a unique valid dirname from template name
114
 * @param string $sName
115
 * @return string
116
 */
117
	private function _SanitizeNewDirName($sName)
118
	{
119
		// remove all of the ASCII controllcodes
120
		$sName = preg_replace('/[^0-9a-zA-Z \-_]/', '', strtolower($sName));
121
		$sName = str_replace(array('-', '_'), array('- ', ' '), $sName);
122
		$sName = str_replace(' ', '', ucwords($sName));
123
		$sName = ($sName == '' ? 'MyNewTheme' : $sName);
124
		$iCount = '';
125
		do {
126
			$sSearch = $sName.($iCount ? '_'.$iCount : '');
127 1648 darkviper
			$sql = 'SELECT COUNT(*) '
128
			     . 'FROM `'.$this->_aGlobals['TablePrefix'].$this->_aGlobals['AddonTable'].'` '
129 1643 darkviper
				 . 'WHERE `directory`=\''.$sSearch.'\' AND `function`=\'theme\'';
130
			$exists = $this->_oDb->get_one($sql);
131
			$iCount = (int)$iCount + 1;
132
		}while($exists);
133
		return ($sName != $sSearch ? $sSearch : $sName);
134
	}
135
/**
136
 * copy the complete theme - directory tree with all included files
137
 * @return boolean true if ok
138
 */
139
	private function _copyTree()
140
	{
141
		$bRetval = true;
142
		$sSourceDir = $this->_sThemesBasePath.'/'.$this->_sSourceDir;
143
		// build file list to copy into destination theme
144
		$aFileList = array();
145
		try {
146
			$oIterator = new RecursiveDirectoryIterator($sSourceDir);
147
			foreach (new RecursiveIteratorIterator($oIterator) as $cur) {
148
				if($cur->isDir() || strpos($cur, '.svn') !== false) { continue; }
149
				$sPattern = '/^'.preg_quote($sSourceDir, '/').'/';
150
				$sFilename = preg_replace($sPattern, '', $cur->getPathname());
151
				$sFilePath = preg_replace($sPattern, '', $cur->getPath());
152
				$aFileList[$sFilename] = $sFilePath;
153
			}
154
			natsort($aFileList);
155
		}catch(Exeption $e) {
156
			$msg  = 'CopyTheme::_copyTree => '.$this->_aLang['GENERIC_FAILED_COMPARE'];
157
			$msg .= '<br />'.$e->getMessage();
158
			$this->_setError($msg);
159
			$bRetval = false;
160
		}
161
		if($bRetval) {
162
		// create all needed directories
163
			$aDirectories = array_unique(array_values($aFileList));
164
			$bRetval = $this->_createDirs($aDirectories);
165
		}
166
		if($bRetval) {
167
			// copy all needed files
168
			$aFiles = array_keys($aFileList);
169
			$bRetval = $this->_copyFiles($aFiles);
170
		}
171
		return $bRetval;
172
	}
173
/**
174
 * create all the directories from $aNewDirs-list under $sBaseDir
175
 * @param array $aNewDirList ( array('', '/images', '/images/files')
176
 */
177
	private function _createDirs($aNewDirList)
178
	{
179
		$bRetval = true;
180
		foreach($aNewDirList as $sDir) {
181
			$sNewDir = $this->_sThemesBasePath.'/'.$this->_sNewThemeDir.$sDir;
182
			if(!file_exists($sNewDir) &&  mkdir($sNewDir, 0777, true)) {
183
				if($this->_aGlobals['IsLinux']) {
184
					if(!chmod($sNewDir, $this->_aGlobals['StringDirMode'])) {
185
						$sMsg = 'CopyTheme::createDirs::chmod('.$sNewDir.') >> '
186
								.$this->_aLang['GENERIC_FAILED_COMPARE'];
187
						$this->_setError($sMsg);
188
						$bRetval = false;
189
						break;
190
					}
191
				}
192
			}else {
193
				$sMsg = 'CopyTheme::createDirs::mkdir('.$sNewDir.') >> '
194
						.$this->_aLang['GENERIC_FAILED_COMPARE'];
195
				$this->_setError($sMsg);
196
				$bRetval = false;
197
				break;
198
			}
199
		}
200
		return $bRetval;
201
	}
202
/**
203
 * copies a list of files from source directory to destination directory
204
 * @param array $aFileList (array('/index.php', '/images/index.php', '/images/files/index.php')
205
 */
206
	private function _copyFiles($aFileList)
207
	{
208
		$bRetval = true;
209
		$sSourceDir = $this->_sThemesBasePath.'/'.$this->_sSourceDir;
210
		$sDestinationDir = $this->_sThemesBasePath.'/'.$this->_sNewThemeDir;
211
		foreach($aFileList as $sFile) {
212
			if(copy($sSourceDir.$sFile, $sDestinationDir.$sFile)) {
213
				if($this->_aGlobals['IsLinux']) {
214
					if(!chmod($sDestinationDir.$sFile, $this->_aGlobals['StringFileMode'])) {
215
						$sMsg = 'CopyTheme::copyFiles::chmod('.$sDestinationDir.$sFile.') >> '
216
								.$this->_aLang['GENERIC_FAILED_COMPARE'];
217
						$this->_setError($sMsg);
218
						$bRetval = false;
219
						break;
220
					}
221
				}
222
			}else {
223
				$sMsg = 'CopyTheme::copyFiles::copy('.$sDestinationDir.$sFile.') >> '
224
						.$this->_aLang['GENERIC_FAILED_COMPARE'];
225
				$this->_setError($sMsg);
226
				$bRetval = false;
227
				break;
228
			}
229
		}
230
		return $bRetval;
231
	}
232
/**
233
 * register a available theme in the database (gets values from info.php)
234
 * @return bool TRUE if ok
235
 */
236
	private function _registerNewTheme()
237
	{
238
		$bRetval = true;
239
		$sThemeInfoFile = $this->_sThemesBasePath.'/'.$this->_sNewThemeDir.'/info.php';
240
		$aVariables = $this->_getThemeInfo($sThemeInfoFile);
241 1648 darkviper
		$sql = 'INSERT INTO `'.$this->_aGlobals['TablePrefix'].$this->_aGlobals['AddonTable'].'` '
242 1643 darkviper
		     . 'SET `type`=\'template\', '
243
		     .     '`function`=\'theme\', '
244
		     .     '`directory`=\''.$aVariables['directory'].'\', '
245
		     .     '`name`=\''.$aVariables['name'].'\', '
246
		     .     '`description`=\''.mysql_real_escape_string($aVariables['description']).'\', '
247
		     .     '`version`=\''.$aVariables['version'].'\', '
248
		     .     '`platform`=\''.$aVariables['platform'].'\', '
249
		     .     '`author`=\''.mysql_real_escape_string($aVariables['author']).'\', '
250
		     .     '`license`=\''.mysql_real_escape_string($aVariables['license']).'\'';
251
		if(!$this->_oDb->query($sql)) {
252
			$sMsg = 'CopyTheme::registerNewTheme('.$this->_sNewThemeName.') >> '
253
			        .$this->_aLang['GENERIC_NOT_UPGRADED'];
254
			$sMsg .= (($this->_aGlobals['Debug']) ? '<br />'.$this->_oDb->get_error() : '');
255
			$this->_setError($sMsg);
256
			$bRetval = false;
257
		}
258
		return $bRetval;
259
	}
260
/**
261
 * modify info-file of copied theme
262
 * @return bool TRUE if ok
263
 */
264
	private function _modifyInfoFile()
265
	{
266
		$bRetval = false;
267
		$sThemeInfoFile = $this->_sThemesBasePath.'/'.$this->_sNewThemeDir.'/info.php';
268
		$aVariables = $this->_getThemeInfo($sThemeInfoFile);
269
		$aVariables['directory']   = $this->_sNewThemeDir;
270
		$aVariables['name']        = $this->_sNewThemeName;
271
		$aVariables['version']     = '0.0.1';
272
		$aVariables['description'] = '(copy): '.$aVariables['description'];
273
		if(file_exists($sThemeInfoFile)) {
274
			$sInfoContent = file_get_contents($sThemeInfoFile);
275
			foreach($aVariables as $key=>$val) {
276
				$sSearch  = '/(\$template_'.$key.'\s*=\s*(["\'])).*?(\2)\s*;/s';
277
				$sReplace = ($val != '' && is_numeric($val[0])) ? '${1}' : '$1';
278
				$sReplace .= $val.'$2;';
279
				$sInfoContent = preg_replace($sSearch, $sReplace, $sInfoContent);
280
			}
281
			// remove trailing "\?\>" to sanitize the php file
282
			$sInfoContent = preg_replace('/\s*?\?>\s*$/', '', $sInfoContent)."\n";
283
			if(file_put_contents($sThemeInfoFile, $sInfoContent) !== false) {
284
				return true;
285
			}
286
		}
287
		$this->_setError('CopyTheme::modifyInfoFile('.$this->_sNewThemeDir.'/info.php) >> '
288
		               .$this->_aLang['GENERIC_NOT_UPGRADED']);
289
		return $bRetval;
290
	}
291
/**
292
 * read old values from old info-file
293
 * @param string $sThemeInfoFile
294
 * @return array
295
 */
296
	private function _getThemeInfo($sThemeInfoFile)
297
	{
298
		if(file_exists($sThemeInfoFile)) {
299
			include $sThemeInfoFile;
300
			return array(
301
				'directory' => $template_directory?$template_directory:null,
302
				'name' => $template_name?$template_name:null,
303
				'version' => $template_version?$template_version:null,
304
				'description' => $template_description?$template_description:null,
305
				'platform' => $template_platform?$template_platform:null,
306
				'author' => $template_author?$template_author:null,
307
				'license' => $template_license?$template_license:null,
308
			);
309
		} else {
310
			return array(
311
				'directory' => null,
312
				'name' => null,
313
				'version' => null,
314
				'description' => null,
315
				'platform' => null,
316
				'author' => null,
317
				'license' => null,
318
			);
319
		}
320
	}
321
/**
322 1648 darkviper
 * activates the new theme
323
 * @return boolean
324
 */
325
	private function _activateTheme()
326
	{
327
		$bRetval = true;
328
		if(!db_update_key_value( $this->_aGlobals['SettingsTable'],
329
		                         'default_theme',
330
		                         $value = $this->_sNewThemeDir))
331
		{
332
			$sMsg = 'CopyTheme::activateTheme('.$this->_sNewThemeName.') >> '
333
			        .$this->_aLang['GENERIC_NOT_UPGRADED'];
334
			$sMsg .= (($this->_aGlobals['Debug']) ? '<br />'.$this->_oDb->get_error() : '');
335
			$this->_setError($sMsg);
336
			$bRetval = false;
337
		}
338
		return $bRetval;
339
	}
340
/**
341 1643 darkviper
 * on Error undo all already done copy/create actions
342
 * @param string $dir
343
 */
344
	private function _doRollback($dir)
345
	{
346
		$iFileErrors = 0;
347
		$iDirErrors  = 0;
348
		$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir),
349
												  RecursiveIteratorIterator::CHILD_FIRST);
350
		foreach ($iterator as $path) {
351
			if ($path->isDir()) {
352
				rmdir($path->__toString());
353
			} else {
354
				unlink($path->__toString());
355
			}
356
		}
357
		unset($path);
358
		unset($iterator);
359
		if(file_exists($dir)) { rmdir($dir); }
360 1648 darkviper
		$sql = 'DELETE FROM `'.$this->_aGlobals['TablePrefix'].$this->_aGlobals['AddonTable'].'` '
361
		     . 'WHERE `directory`=\''.$this->_sNewThemeDir.'\' AND `function`=\'theme\'';
362
		$this->_oDb->query($sql);
363 1643 darkviper
	}
364
/**
365
 * add a new error message to the queue
366
 * @param string $sErrMsg
367
 */
368
	private function _setError($sErrMsg)
369
	{
370
		$this->_aErrors[] = $sErrMsg;
371
	}
372
/**
373
 * clear the errors queue
374
 */
375
	public function clearError()
376
	{
377
		$this->_aErrors = array();
378
	}
379
/**
380
 * true if there is an error otherwise false
381
 * @return bool
382
 */
383
	public function isError()
384
	{
385
		return (sizeof($this->_aErrors) > 0);
386
	}
387
/**
388
 * gets all error messages as multiline string
389
 * @param bool $nl2br true seperates lines by "<br />" else by "\n"
390
 * @return string
391
 */
392
	public function getError($nl2br = true)
393
	{
394
		$sClue = ($nl2br ? '<br />' : "\n");
395
		$sErrorMsg = implode($sClue, $this->_aErrors);
396
		return $sErrorMsg;
397
	}
398
399
400
} // end class