Project

General

Profile

1
<?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: 1868 $
11
 * @filesource		$HeadURL: svn://isteam.dynxs.de/wb-archiv/branches/2.8.x/wb/admin/addons/CopyTheme.php $
12
 * @lastmodified    $Date: 2013-02-19 22:07:19 +0100 (Tue, 19 Feb 2013) $
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
		$this->_aGlobals['AddonTable']    = 'addons';
42
		$this->_aGlobals['SettingsTable'] = 'settings';
43

    
44
		$this->_aGlobals['Debug']          = (defined('DEBUG') && DEBUG === true);
45
		$this->_aGlobals['IsLinux']        = ((substr(__FILE__, 0, 1)) == '/');
46
		$this->_aGlobals['DirMode']       = octdec(STRING_DIR_MODE);
47
		$this->_aGlobals['FileMode']      = octdec(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
					if($this->_activateTheme()) {
69
						return true;
70
					}
71
				}
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
		$sName = (trim($sName) == '' ? 'MyNewTheme' : $sName);
100
		$sName = $this->_oDb->escapeString($sName);
101
		$iCount = '';
102
		do {
103
			$sSearch = $sName.($iCount ? ' '.$iCount : '');
104
			$sql = 'SELECT COUNT(*) '
105
			     . 'FROM `'.$this->_aGlobals['TablePrefix'].$this->_aGlobals['AddonTable'].'` '
106
				 . '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
			$sql = 'SELECT COUNT(*) '
128
			     . 'FROM `'.$this->_aGlobals['TablePrefix'].$this->_aGlobals['AddonTable'].'` '
129
				 . '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  = $this->_aLang['GENERIC_FAILED_COMPARE'];
157
			if($this->_aGlobals['Debug']) {
158
				$msg .= '<br />'.__CLASS__.'::'.__METHOD__. '<br />'.$e->getMessage();
159
			}
160
			$this->_setError($msg);
161
			$bRetval = false;
162
		}
163
		if($bRetval) {
164
		// create all needed directories
165
			$aDirectories = array_unique(array_values($aFileList));
166
			$bRetval = $this->_createDirs($aDirectories);
167
		}
168
		if($bRetval) {
169
			// copy all needed files
170
			$aFiles = array_keys($aFileList);
171
			$bRetval = $this->_copyFiles($aFiles);
172
		}
173
		return $bRetval;
174
	}
175
/**
176
 * create all the directories from $aNewDirs-list under $sBaseDir
177
 * @param array $aNewDirList ( array('', '/images', '/images/files')
178
 */
179
	private function _createDirs($aNewDirList)
180
	{
181
		$bRetval = true;
182
		foreach($aNewDirList as $sDir) {
183
			$sNewDir = $this->_sThemesBasePath.'/'.$this->_sNewThemeDir.$sDir;
184
			if(!file_exists($sNewDir) &&  mkdir($sNewDir, 0777, true)) {
185
				if($this->_aGlobals['IsLinux']) {
186
					if(!chmod($sNewDir, $this->_aGlobals['DirMode'])) {
187
						$sMsg = $this->_aLang['UPLOAD_ERR_CANT_WRITE'].' ['.$sNewDir.']';
188
						if($this->_aGlobals['Debug']) {
189
							$sMsg .= '<br />'.__CLASS__.'::'.__METHOD__.'::'
190
								 . 'chmod(\''.$sNewDir.$_sThemePath.$sFile.'\', '
191
								 . decoct($this->_aGlobals['DirMode']).')';
192
						}
193
						$this->_setError($sMsg);
194
						$bRetval = false;
195
						break;
196
					}
197
				}
198
			}else {
199
				$sMsg = $this->_aLang['GENERIC_FAILED_COMPARE'].' ['.$sFile.']';
200
				if($this->_aGlobals['Debug']) {
201
					$sMsg .= '<br />'.__CLASS__.'::'.__METHOD__.'::'
202
						 . 'mkdir(\''.$sNewDir.'\', 0777, true)';
203
				}
204
				$this->_setError($sMsg);
205
				$bRetval = false;
206
				break;
207
			}
208
		}
209
		return $bRetval;
210
	}
211
/**
212
 * copies a list of files from source directory to destination directory
213
 * @param array $aFileList (array('/index.php', '/images/index.php', '/images/files/index.php')
214
 */
215
	private function _copyFiles($aFileList)
216
	{
217
		$bRetval = true;
218
		$sSourceDir = $this->_sThemesBasePath.'/'.$this->_sSourceDir;
219
		$sDestinationDir = $this->_sThemesBasePath.'/'.$this->_sNewThemeDir;
220
		foreach($aFileList as $sFile) {
221
			if(copy($sSourceDir.$sFile, $sDestinationDir.$sFile)) {
222
				if($this->_aGlobals['IsLinux']) {
223
					if(!chmod($sDestinationDir.$sFile, $this->_aGlobals['FileMode'])) {
224
						$sMsg = $this->_aLang['GENERIC_FAILED_COMPARE'].' ['.$sFile.']';
225
						if($this->_aGlobals['Debug']) {
226
							$sMsg .= '<br />'.__CLASS__.'::'.__METHOD__.'::'
227
								  . 'chmod(\''.$sDestinationDir.$sFile.'\', '
228
								  .decoct($this->_aGlobals['FileMode']).', true)';
229
						}
230
						$this->_setError($sMsg);
231
						$bRetval = false;
232
						break;
233
					}
234
				}
235
			}else {
236
				$sMsg = $this->_aLang['GENERIC_FAILED_COMPARE'].' ['.$sFile.']';
237
				if($this->_aGlobals['Debug']) {
238
					$sMsg .= '<br />'.__CLASS__.'::'.__METHOD__.'::'
239
						  . 'copy(\''.$sSourceDir.$sFile.'\', '
240
					      . '\''.$sDestinationDir.$sFile.'\')';
241
				}
242
				$this->_setError($sMsg);
243
				$bRetval = false;
244
				break;
245
			}
246
		}
247
		return $bRetval;
248
	}
249
/**
250
 * register a available theme in the database (gets values from info.php)
251
 * @return bool TRUE if ok
252
 */
253
	private function _registerNewTheme()
254
	{
255
		$bRetval = true;
256
		$sThemeInfoFile = $this->_sThemesBasePath.'/'.$this->_sNewThemeDir.'/info.php';
257
		$aVariables = $this->_getThemeInfo($sThemeInfoFile);
258
		$sql = 'INSERT INTO `'.$this->_aGlobals['TablePrefix'].$this->_aGlobals['AddonTable'].'` '
259
		     . 'SET `type`=\'template\', '
260
		     .     '`function`=\'theme\', '
261
		     .     '`directory`=\''.$aVariables['directory'].'\', '
262
		     .     '`name`=\''.$aVariables['name'].'\', '
263
		     .     '`description`=\''.$this->_oDb->escapeString($aVariables['description']).'\', '
264
		     .     '`version`=\''.$aVariables['version'].'\', '
265
		     .     '`platform`=\''.$aVariables['platform'].'\', '
266
		     .     '`author`=\''.$this->_oDb->escapeString($aVariables['author']).'\', '
267
		     .     '`license`=\''.$this->_oDb->escapeString($aVariables['license']).'\'';
268
		if(!$this->_oDb->query($sql)) {
269

    
270
			$sMsg = $this->_aLang['GENERIC_NOT_UPGRADED'].' ['.$this->_sNewThemeDir.'/info.php]';
271
			if($this->_aGlobals['Debug']) {
272
				$sMsg .= '<br />'.__CLASS__.'::'.__METHOD__. '<br />'.$this->_oDb->get_error();
273
			}
274
			$this->_setError($sMsg);
275
			$bRetval = false;
276
		}
277
		return $bRetval;
278
	}
279
/**
280
 * modify info-file of copied theme
281
 * @return bool TRUE if ok
282
 */
283
	private function _modifyInfoFile()
284
	{
285
		$bRetval = false;
286
		$sThemeInfoFile = $this->_sThemesBasePath.'/'.$this->_sNewThemeDir.'/info.php';
287
		$aVariables = $this->_getThemeInfo($sThemeInfoFile);
288
		$aVariables['directory']   = $this->_sNewThemeDir;
289
		$aVariables['name']        = $this->_sNewThemeName;
290
		$aVariables['version']     = '0.0.1';
291
		$aVariables['description'] = '(copy): '.$aVariables['description'];
292
		if(is_writeable($sThemeInfoFile)) {
293
			$sInfoContent = file_get_contents($sThemeInfoFile);
294
			foreach($aVariables as $key=>$val) {
295
				$sSearch  = '/(\$template_'.$key.'\s*=\s*(["\'])).*?(\2)\s*;/s';
296
				$sReplace = ($val != '' && is_numeric($val[0])) ? '${1}' : '$1';
297
				$sReplace .= $val.'$2;';
298
				$sInfoContent = preg_replace($sSearch, $sReplace, $sInfoContent);
299
			}
300
			// remove trailing "\?\>" to sanitize the php file
301
			$sInfoContent = preg_replace('/\s*?\?>\s*$/', '', $sInfoContent)."\n";
302
			if(file_put_contents($sThemeInfoFile, $sInfoContent) !== false) {
303
				return true;
304
			}
305
		}
306
		$sMsg = $this->_aLang['GENERIC_NOT_UPGRADED'].' ['.$this->_sNewThemeDir.'/info.php]';
307
		if($this->_aGlobals['Debug']) {
308
			$sMsg .= '<br />'.__CLASS__.'::'.__METHOD__.'(\''.$sThemeInfoFile.'\')';
309
		}
310
		$this->_setError($sMsg);
311
		return $bRetval;
312
	}
313
/**
314
 * read old values from old info-file
315
 * @param string $sThemeInfoFile
316
 * @return array
317
 */
318
	private function _getThemeInfo($sThemeInfoFile)
319
	{
320
		if(file_exists($sThemeInfoFile)) {
321
			include $sThemeInfoFile;
322
			return array(
323
				'directory' => $template_directory?$template_directory:null,
324
				'name' => $template_name?$template_name:null,
325
				'version' => $template_version?$template_version:null,
326
				'description' => $template_description?$template_description:null,
327
				'platform' => $template_platform?$template_platform:null,
328
				'author' => $template_author?$template_author:null,
329
				'license' => $template_license?$template_license:null,
330
			);
331
		} else {
332
			return array(
333
				'directory' => null,
334
				'name' => null,
335
				'version' => null,
336
				'description' => null,
337
				'platform' => null,
338
				'author' => null,
339
				'license' => null,
340
			);
341
		}
342
	}
343
/**
344
 * activates the new theme
345
 * @return boolean
346
 */
347
	private function _activateTheme()
348
	{
349
		$bRetval = true;
350
		if(!db_update_key_value( $this->_aGlobals['SettingsTable'],
351
		                         'default_theme',
352
		                         $value = $this->_sNewThemeDir))
353
		{
354
			$sMsg = $this->_aLang['GENERIC_NOT_UPGRADED'];
355
			if($this->_aGlobals['Debug']) {
356
				$sMsg .= '<br />'.__CLASS__.'::'.__METHOD__. '<br />'.$this->_oDb->get_error();
357
			}
358
			$this->_setError($sMsg);
359
			$bRetval = false;
360
		}
361
		return $bRetval;
362
	}
363
/**
364
 * on Error undo all already done copy/create actions
365
 * @param string $dir
366
 */
367
	private function _doRollback($dir)
368
	{
369
		$iFileErrors = 0;
370
		$iDirErrors  = 0;
371
		$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir),
372
												  RecursiveIteratorIterator::CHILD_FIRST);
373
		foreach ($iterator as $path) {
374
			if ($path->isDir()) {
375
				rmdir($path->__toString());
376
			} else {
377
				unlink($path->__toString());
378
			}
379
		}
380
		unset($path);
381
		unset($iterator);
382
		if(file_exists($dir)) { rmdir($dir); }
383
		$sql = 'DELETE FROM `'.$this->_aGlobals['TablePrefix'].$this->_aGlobals['AddonTable'].'` '
384
		     . 'WHERE `directory`=\''.$this->_sNewThemeDir.'\' AND `function`=\'theme\'';
385
		$this->_oDb->query($sql);
386
	}
387
/**
388
 * add a new error message to the queue
389
 * @param string $sErrMsg
390
 */
391
	private function _setError($sErrMsg)
392
	{
393
		$this->_aErrors[] = $sErrMsg;
394
	}
395
/**
396
 * clear the errors queue
397
 */
398
	public function clearError()
399
	{
400
		$this->_aErrors = array();
401
	}
402
/**
403
 * true if there is an error otherwise false
404
 * @return bool
405
 */
406
	public function isError()
407
	{
408
		return (sizeof($this->_aErrors) > 0);
409
	}
410
/**
411
 * gets all error messages as multiline string
412
 * @param bool $nl2br true seperates lines by "<br />" else by "\n"
413
 * @return string 
414
 */
415
	public function getError($nl2br = true)
416
	{
417
		$sClue = ($nl2br ? '<br />' : "\n");
418
		$sErrorMsg = implode($sClue, $this->_aErrors);
419
		return $sErrorMsg;
420
	}
421

    
422

    
423
} // end class
(1-1/4)