Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php/*** TThemeManager class** @author Qiang Xue <qiang.xue@gmail.com>* @link http://www.pradosoft.com/* @copyright Copyright © 2005-2008 PradoSoft* @license http://www.pradosoft.com/license/* @version $Id: TThemeManager.php 2497 2008-08-14 21:15:30Z knut $* @package System.Web.UI*/Prado::using('System.Web.Services.TPageService');/*** TThemeManager class** TThemeManager manages the themes used in a Prado application.** Themes are stored under the directory specified by the* {@link setBasePath BasePath} property. The themes can be accessed* via URL {@link setBaseUrl BaseUrl}. Each theme is represented by a subdirectory* and all the files under that directory. The name of a theme is the name* of the corresponding subdirectory.* By default, the base path of all themes is a directory named "themes"* under the directory containing the application entry script.* To get a theme (normally you do not need to), call {@link getTheme}.** TThemeManager may be configured within page service tag in application* configuration file as follows,* <module id="themes" class="System.Web.UI.TThemeManager"* BasePath="Application.themes" BaseUrl="/themes" />* where {@link getCacheExpire CacheExpire}, {@link getCacheControl CacheControl}* and {@link getBufferOutput BufferOutput} are configurable properties of THttpResponse.** @author Qiang Xue <qiang.xue@gmail.com>* @version $Id: TThemeManager.php 2497 2008-08-14 21:15:30Z knut $* @package System.Web.UI* @since 3.0*/class TThemeManager extends TModule{/*** default themes base path*/const DEFAULT_BASEPATH='themes';/*** @var boolean whether this module has been initialized*/private $_initialized=false;/*** @var string the directory containing all themes*/private $_basePath=null;/*** @var string the base URL for all themes*/private $_baseUrl=null;/*** Initializes the module.* This method is required by IModule and is invoked by application.* @param TXmlElement module configuration*/public function init($config){$this->_initialized=true;$service=$this->getService();if($service instanceof TPageService)$service->setThemeManager($this);elsethrow new TConfigurationException('thememanager_service_unavailable');}/*** @param string name of the theme to be retrieved* @return TTheme the theme retrieved*/public function getTheme($name){$themePath=$this->getBasePath().DIRECTORY_SEPARATOR.$name;$themeUrl=rtrim($this->getBaseUrl(),'/').'/'.$name;return new TTheme($themePath,$themeUrl);}/*** @return array list of available theme names*/public function getAvailableThemes(){$themes=array();$basePath=$this->getBasePath();$folder=@opendir($basePath);while($file=@readdir($folder)){if($file!=='.' && $file!=='..' && $file!=='.svn' && is_dir($basePath.DIRECTORY_SEPARATOR.$file))$themes[]=$file;}closedir($folder);return $themes;}/*** @return string the base path for all themes. It is returned as an absolute path.* @throws TConfigurationException if base path is not set and "themes" directory does not exist.*/public function getBasePath(){if($this->_basePath===null){$this->_basePath=dirname($this->getRequest()->getApplicationFilePath()).DIRECTORY_SEPARATOR.self::DEFAULT_BASEPATH;if(($basePath=realpath($this->_basePath))===false || !is_dir($basePath))throw new TConfigurationException('thememanager_basepath_invalid2',$this->_basePath);$this->_basePath=$basePath;}return $this->_basePath;}/*** @param string the base path for all themes. It must be in the format of a namespace.* @throws TInvalidDataValueException if the base path is not a proper namespace.*/public function setBasePath($value){if($this->_initialized)throw new TInvalidOperationException('thememanager_basepath_unchangeable');else{$this->_basePath=Prado::getPathOfNamespace($value);if($this->_basePath===null || !is_dir($this->_basePath))throw new TInvalidDataValueException('thememanager_basepath_invalid',$value);}}/*** @return string the base URL for all themes.* @throws TConfigurationException If base URL is not set and a correct one cannot be determined by Prado.*/public function getBaseUrl(){if($this->_baseUrl===null){$appPath=dirname($this->getRequest()->getApplicationFilePath());$basePath=$this->getBasePath();if(strpos($basePath,$appPath)===false)throw new TConfigurationException('thememanager_baseurl_required');$appUrl=rtrim(dirname($this->getRequest()->getApplicationUrl()),'/\\');$this->_baseUrl=$appUrl.strtr(substr($basePath,strlen($appPath)),'\\','/');}return $this->_baseUrl;}/*** @param string the base URL for all themes.*/public function setBaseUrl($value){$this->_baseUrl=rtrim($value,'/');}}/*** TTheme class** TTheme represents a particular theme. It is merely a collection of skins* that are applicable to the corresponding controls.** Each theme is stored as a directory and files under that directory.* The theme name is the directory name. When TTheme is created, the files* whose name has the extension ".skin" are parsed and saved as controls skins.** A skin is essentially a list of initial property values that are to be applied* to a control when the skin is applied.* Each type of control can have multiple skins identified by the SkinID.* If a skin does not have SkinID, it is the default skin that will be applied* to controls that do not specify particular SkinID.** Whenever possible, TTheme will try to make use of available cache to save* the parsing time.** To apply a theme to a particular control, call {@link applySkin}.** @author Qiang Xue <qiang.xue@gmail.com>* @version $Id: TThemeManager.php 2497 2008-08-14 21:15:30Z knut $* @package System.Web.UI* @since 3.0*/class TTheme extends TApplicationComponent implements ITheme{/*** prefix for cache variable name used to store parsed themes*/const THEME_CACHE_PREFIX='prado:theme:';/*** Extension name of skin files*/const SKIN_FILE_EXT='.skin';/*** @var string theme path*/private $_themePath;/*** @var string theme url*/private $_themeUrl;/*** @var array list of skins for the theme*/private $_skins=null;/*** @var string theme name*/private $_name='';/*** @var array list of css files*/private $_cssFiles=array();/*** @var array list of js files*/private $_jsFiles=array();/*** Constructor.* @param string theme path* @param string theme URL* @throws TConfigurationException if theme path does not exist or any parsing error of the skin files*/public function __construct($themePath,$themeUrl){$this->_themeUrl=$themeUrl;$this->_themePath=realpath($themePath);$this->_name=basename($themePath);$cacheValid=false;// TODO: the following needs to be cleaned up (Qiang)if(($cache=$this->getApplication()->getCache())!==null){$array=$cache->get(self::THEME_CACHE_PREFIX.$themePath);if(is_array($array)){list($skins,$cssFiles,$jsFiles,$timestamp)=$array;if($this->getApplication()->getMode()!==TApplicationMode::Performance){if(($dir=opendir($themePath))===false)throw new TIOException('theme_path_inexistent',$themePath);$cacheValid=true;while(($file=readdir($dir))!==false){if($file==='.' || $file==='..')continue;else if(basename($file,'.css')!==$file)$this->_cssFiles[]=$themeUrl.'/'.$file;else if(basename($file,'.js')!==$file)$this->_jsFiles[]=$themeUrl.'/'.$file;else if(basename($file,self::SKIN_FILE_EXT)!==$file && filemtime($themePath.DIRECTORY_SEPARATOR.$file)>$timestamp){$cacheValid=false;break;}}closedir($dir);if($cacheValid)$this->_skins=$skins;}else{$cacheValid=true;$this->_cssFiles=$cssFiles;$this->_jsFiles=$jsFiles;$this->_skins=$skins;}}}if(!$cacheValid){$this->_cssFiles=array();$this->_jsFiles=array();$this->_skins=array();if(($dir=opendir($themePath))===false)throw new TIOException('theme_path_inexistent',$themePath);while(($file=readdir($dir))!==false){if($file==='.' || $file==='..')continue;else if(basename($file,'.css')!==$file)$this->_cssFiles[]=$themeUrl.'/'.$file;else if(basename($file,'.js')!==$file)$this->_jsFiles[]=$themeUrl.'/'.$file;else if(basename($file,self::SKIN_FILE_EXT)!==$file){$template=new TTemplate(file_get_contents($themePath.'/'.$file),$themePath,$themePath.'/'.$file);foreach($template->getItems() as $skin){if(!isset($skin[2])) // a text string, ignoredcontinue;else if($skin[0]!==-1)throw new TConfigurationException('theme_control_nested',$skin[1],dirname($themePath));$type=$skin[1];$id=isset($skin[2]['skinid'])?$skin[2]['skinid']:0;unset($skin[2]['skinid']);if(isset($this->_skins[$type][$id]))throw new TConfigurationException('theme_skinid_duplicated',$type,$id,dirname($themePath));/*foreach($skin[2] as $name=>$value){if(is_array($value) && ($value[0]===TTemplate::CONFIG_DATABIND || $value[0]===TTemplate::CONFIG_PARAMETER))throw new TConfigurationException('theme_databind_forbidden',dirname($themePath),$type,$id);}*/$this->_skins[$type][$id]=$skin[2];}}}closedir($dir);sort($this->_cssFiles);sort($this->_jsFiles);if($cache!==null)$cache->set(self::THEME_CACHE_PREFIX.$themePath,array($this->_skins,$this->_cssFiles,$this->_jsFiles,time()));}}/*** @return string theme name*/public function getName(){return $this->_name;}/*** @param string theme name*/protected function setName($value){$this->_name = $value;}/*** @return string the URL to the theme folder (without ending slash)*/public function getBaseUrl(){return $this->_themeUrl;}/*** @param string the URL to the theme folder*/protected function setBaseUrl($value){$this->_themeUrl=rtrim($value,'/');}/*** @return string the file path to the theme folder*/public function getBasePath(){return $this->_themePath;}/*** @param string tthe file path to the theme folder*/protected function setBasePath($value){$this->_themePath=$value;}/*** @return array list of skins for the theme*/public function getSkins(){return $this->_skins;}/*** @param array list of skins for the theme*/protected function setSkins($value){$this->_skins = $value;}/*** Applies the theme to a particular control.* The control's class name and SkinID value will be used to* identify which skin to be applied. If the control's SkinID is empty,* the default skin will be applied.* @param TControl the control to be applied with a skin* @return boolean if a skin is successfully applied* @throws TConfigurationException if any error happened during the skin application*/public function applySkin($control){$type=get_class($control);if(($id=$control->getSkinID())==='')$id=0;if(isset($this->_skins[$type][$id])){foreach($this->_skins[$type][$id] as $name=>$value){Prado::trace("Applying skin $name to $type",'System.Web.UI.TThemeManager');if(is_array($value)){switch($value[0]){case TTemplate::CONFIG_EXPRESSION:$value=$this->evaluateExpression($value[1]);break;case TTemplate::CONFIG_ASSET:$value=$this->_themeUrl.'/'.ltrim($value[1],'/');break;case TTemplate::CONFIG_DATABIND:$control->bindProperty($name,$value[1]);break;case TTemplate::CONFIG_PARAMETER:$control->setSubProperty($name,$this->getApplication()->getParameters()->itemAt($value[1]));break;case TTemplate::CONFIG_TEMPLATE:$control->setSubProperty($name,$value[1]);break;case TTemplate::CONFIG_LOCALIZATION:$control->setSubProperty($name,Prado::localize($value[1]));break;default:throw new TConfigurationException('theme_tag_unexpected',$name,$value[0]);break;}}if(!is_array($value)){if(strpos($name,'.')===false) // is simple property or custom attribute{if($control->hasProperty($name)){if($control->canSetProperty($name)){$setter='set'.$name;$control->$setter($value);}elsethrow new TConfigurationException('theme_property_readonly',$type,$name);}elsethrow new TConfigurationException('theme_property_undefined',$type,$name);}else // complex property$control->setSubProperty($name,$value);}}return true;}elsereturn false;}/*** @return array list of CSS files (URL) in the theme*/public function getStyleSheetFiles(){return $this->_cssFiles;}/*** @param array list of CSS files (URL) in the theme*/protected function setStyleSheetFiles($value){$this->_cssFiles=$value;}/*** @return array list of Javascript files (URL) in the theme*/public function getJavaScriptFiles(){return $this->_jsFiles;}/*** @param array list of Javascript files (URL) in the theme*/protected function setJavaScriptFiles($value){$this->_jsFiles=$value;}}?>