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