diff options
Diffstat (limited to 'framework')
-rw-r--r-- | framework/Web/UI/TControl.php | 12 | ||||
-rw-r--r-- | framework/Web/UI/TPage.php | 6 | ||||
-rw-r--r-- | framework/Web/UI/TTheme.php | 160 | ||||
-rw-r--r-- | framework/Web/UI/TThemeManager.php | 204 |
4 files changed, 349 insertions, 33 deletions
diff --git a/framework/Web/UI/TControl.php b/framework/Web/UI/TControl.php index b0d353f3..21c633c7 100644 --- a/framework/Web/UI/TControl.php +++ b/framework/Web/UI/TControl.php @@ -263,6 +263,14 @@ class TControl extends TComponent }
/**
+ * @return TPageService the page service
+ */
+ public function getService()
+ {
+ return Prado::getApplication()->getService();
+ }
+
+ /**
* @return THttpRequest the current user request
*/
public function getRequest()
@@ -296,12 +304,12 @@ class TControl extends TComponent public function publishFile($file)
{
- return Prado::getApplication()->getService()->getAssetManager()->publishFile($file);
+ return $this->getService()->getAssetManager()->publishFile($file);
}
public function publishDirectory($directory)
{
- return Prado::getApplication()->getService()->getAssetManager()->publishDirectory($directory);
+ return $this->getService()->getAssetManager()->publishDirectory($directory);
}
public function getAsset($assetName)
diff --git a/framework/Web/UI/TPage.php b/framework/Web/UI/TPage.php index 08fbd2fe..232878da 100644 --- a/framework/Web/UI/TPage.php +++ b/framework/Web/UI/TPage.php @@ -66,7 +66,7 @@ class TPage extends TTemplateControl return parent::loadTemplate();
else
{
- $template=Prado::getApplication()->getService()->getTemplateManager()->loadTemplateByFileName(Prado::getPathOfNamespace($this->_templateFile,'.tpl'));
+ $template=Prado::getApplication()->getService()->getTemplateManager()->getTemplateByFileName(Prado::getPathOfNamespace($this->_templateFile,'.tpl'));
$this->setTemplate($template);
return $template;
}
@@ -326,9 +326,9 @@ EOD; private function initializeThemes()
{
if($this->_themeName!=='')
- $this->_theme=new TTheme($this->_themeName);
+ $this->_theme=$this->getService()->getThemeManager()->getTheme($this->_themeName);
if($this->_styleSheetName!=='')
- $this->_styleSheet=new TTheme($this->_styleSheetName);
+ $this->_styleSheet=$this->getService()->getThemeManager()->getTheme($this->_styleSheetName);
}
/**
diff --git a/framework/Web/UI/TTheme.php b/framework/Web/UI/TTheme.php index 3d4d4e5d..452c473c 100644 --- a/framework/Web/UI/TTheme.php +++ b/framework/Web/UI/TTheme.php @@ -1,46 +1,129 @@ <?php
-class TTheme extends TComponent
+class TThemeManager extends TComponent implements IModule
{
- private $_themePath;
- private $_skins=array();
+ const THEME_CACHE_PREFIX='prado:theme:';
+ const DEFAULT_THEME_PATH='themes';
+ const THEME_FILE='theme.tpl';
+ /**
+ * @var string module ID
+ */
+ private $_id;
+ /**
+ * @var boolean whether this module has been initialized
+ */
+ private $_initialized=false;
+ /**
+ * @var string the directory containing all themes
+ */
+ private $_themePath=null;
+ /**
+ * @var IApplication application
+ */
+ private $_application;
- public function __construct($name)
+ /**
+ * Initializes the module.
+ * This method is required by IModule and is invoked by application.
+ * @param IApplication application
+ * @param TXmlElement module configuration
+ */
+ public function init($application,$config)
{
- $this->_themePath=$name;
- $this->initialize();
+ $this->_application=$application;
+ if($this->_themePath===null)
+ $this->_themePath=dirname($application->getConfigurationFile()).'/'.self::DEFAULT_THEME_PATH;
+
+ $this->_initialized=true;
}
- private function initialize()
+ /**
+ * @return string id of this module
+ */
+ public function getID()
{
- if(($theme=opendir($this->_themePath))===false)
- throw new Exception("Invalid theme ".$this->_themePath);
- while(($file=readdir($theme))!==false)
+ return $this->_id;
+ }
+
+ /**
+ * @param string id of this module
+ */
+ public function setID($value)
+ {
+ $this->_id=$value;
+ }
+
+ public function getTheme($name)
+ {
+ $themePath=realpath($this->_themePath.'/'.$name);
+ if($themePath===false || !is_dir($this->_themePath))
+ throw new TConfigurationException('thememanager_themepath_invalid',$themePath);
+ if(($cache=$this->_application->getCache())===null)
+ return new TTheme($themePath.'/'.self::THEME_FILE);
+ else
{
- if(basename($file,'.skin')!==$file)
- $this->parseSkinFile($this->_themePath.'/'.$file);
+ $themeFile=$themePath.'/'.self::THEME_FILE;
+ $array=$cache->get(self::THEME_CACHE_PREFIX.$themePath);
+ if(is_array($array))
+ {
+ list($theme,$timestamp)=$array;
+ if(filemtime($themeFile)<$timestamp)
+ return $theme;
+ }
+ $theme=new TTheme($themeFile);
+ $cache->set(self::THEME_CACHE_PREFIX.$themePath,array($theme,time()));
+ return $theme;
}
- closedir($theme);
}
- private function parseSkinFile($fileName)
+ public function getThemePath()
{
- if(($skin=simplexml_load_file($fileName))===false)
- throw new Exception("Parsing $fileName failed.");
- foreach($skin->children() as $type=>$control)
+ return $this->_themePath;
+ }
+
+ public function setThemePath($value)
+ {
+ if($this->_initialized)
+ throw new TInvalidOperationException('thememanager_themepath_unchangeable');
+ else
+ $this->_themePath=$value;
+ }
+}
+
+class TTheme extends TTemplate
+{
+ private $_themePath;
+ private $_themeFile;
+ private $_skins=array();
+
+ public function __construct($themeFile)
+ {
+ $this->_themeFile=$themeFile;
+ $this->_themePath=dirname($themeFile);
+ if(is_file($themeFile) && is_readable($themeFile))
{
- $attributes=array();
- foreach($control->attributes() as $name=>$value)
+ $theme=&$this->parse(file_get_contents($themeFile));
+ foreach($theme as $skin)
{
- $attributes[strtolower($name)]=(string)$value;
+ if($skin[0]!==0)
+ throw new TConfigurationException('theme_control_nested',$skin[1]);
+ else if(!isset($skin[2])) // a text string, ignored
+ continue;
+ $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);
+ foreach($skin[2] as $name=>$value)
+ {
+ if(is_array($value) && $value[0]===0)
+ throw new TConfigurationException('theme_databind_unsupported',$type,$id,$name);
+ }
+ $this->_skins[$type][$id]=$skin[2];
}
- $skinID=isset($attributes['skinid'])?(string)$attributes['skinid']:0;
- unset($attributes['skinid']);
- if(isset($this->_skins[$type][$skinID]))
- throw new Exception("Duplicated skin $type.$skinID");
- else
- $this->_skins[$type][$skinID]=$attributes;
}
+ else
+ throw new TIOException('theme_themefile_invalid',$themeFile);
}
public function applySkin($control)
@@ -52,11 +135,32 @@ class TTheme extends TComponent {
foreach($this->_skins[$type][$id] as $name=>$value)
{
- $control->setSubProperty($name,$value);
+ if(is_array($value))
+ $value=$this->evaluateExpression($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);
+ }
+ else
+ throw new TConfigurationException('theme_property_readonly',get_class($control),$name);
+ }
+ else if($control->getAllowCustomAttributes())
+ $control->getAttributes()->add($name,$value);
+ else
+ throw new TConfigurationException('theme_property_undefined',get_class($control),$name);
+ }
+ else // complex property
+ $control->setSubProperty($name,$value);
}
+ return true;
}
else
- return;
+ return false;
}
}
diff --git a/framework/Web/UI/TThemeManager.php b/framework/Web/UI/TThemeManager.php new file mode 100644 index 00000000..8edae2e1 --- /dev/null +++ b/framework/Web/UI/TThemeManager.php @@ -0,0 +1,204 @@ +<?php
+
+class TThemeManager extends TComponent implements IModule
+{
+ const THEME_CACHE_PREFIX='prado:theme:';
+ const DEFAULT_THEME_PATH='themes';
+ /**
+ * @var string module ID
+ */
+ private $_id;
+ /**
+ * @var boolean whether this module has been initialized
+ */
+ private $_initialized=false;
+ /**
+ * @var string the directory containing all themes
+ */
+ private $_themePath=null;
+ /**
+ * @var IApplication application
+ */
+ private $_application;
+
+ /**
+ * Initializes the module.
+ * This method is required by IModule and is invoked by application.
+ * @param IApplication application
+ * @param TXmlElement module configuration
+ */
+ public function init($application,$config)
+ {
+ $this->_application=$application;
+ if($this->_themePath===null)
+ $this->_themePath=dirname($application->getConfigurationFile()).'/'.self::DEFAULT_THEME_PATH;
+
+ $this->_initialized=true;
+ }
+
+ /**
+ * @return string id of this module
+ */
+ public function getID()
+ {
+ return $this->_id;
+ }
+
+ /**
+ * @param string id of this module
+ */
+ public function setID($value)
+ {
+ $this->_id=$value;
+ }
+
+ public function getTheme($name)
+ {
+ $themePath=realpath($this->_themePath.'/'.$name);
+ if($themePath===false || !is_dir($this->_themePath))
+ throw new TConfigurationException('thememanager_themepath_invalid',$themePath);
+ if(($cache=$this->_application->getCache())!==null)
+ {
+ $array=$cache->get(self::THEME_CACHE_PREFIX.$themePath);
+ if(is_array($array))
+ {
+ list($theme,$timestamp)=$array;
+ $cacheValid=true;
+ if(($dir=opendir($themePath))===false)
+ throw new TIOException('thememanager_themepath_invalid',$themePath);
+ while(($file=readdir($dir))!==false)
+ {
+ if(basename($file,'.skin')!==$file && filemtime($themePath.'/'.$file)>$timestamp)
+ {
+ $cacheValid=false;
+ break;
+ }
+ }
+ closedir($dir);
+ if($cacheValid)
+ return $theme;
+ }
+ }
+ // not cached, so we parse all skin files
+ $content='';
+ if(($dir=opendir($themePath))===false)
+ throw new TIOException('thememanager_themepath_invalid',$themePath);
+ while(($file=readdir($dir))!==false)
+ {
+ if(basename($file,'.skin')!==$file)
+ $content.=file_get_contents($themePath.'/'.$file);
+ }
+ closedir($dir);
+ $theme=new TTheme($content,$themePath);
+ if($cache!==null)
+ $cache->set(self::THEME_CACHE_PREFIX.$themePath,array($theme,time()));
+ return $theme;
+ }
+
+ public function getThemePath()
+ {
+ return $this->_themePath;
+ }
+
+ public function setThemePath($value)
+ {
+ if($this->_initialized)
+ throw new TInvalidOperationException('thememanager_themepath_unchangeable');
+ else
+ $this->_themePath=$value;
+ }
+}
+
+class TTheme extends TTemplate
+{
+ const ASSET_PATH='assets';
+ private $_themePath;
+ private $_themeFile;
+ private $_skins=array();
+
+ public function __construct($content,$themePath)
+ {
+ $this->_themePath=$themePath;
+ $theme=&$this->parse($content);
+ foreach($theme as $skin)
+ {
+ if($skin[0]!==-1)
+ throw new TConfigurationException('theme_control_nested',$skin[1]);
+ else if(!isset($skin[2])) // a text string, ignored
+ continue;
+ $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);
+ foreach($skin[2] as $name=>$value)
+ {
+ if(is_array($value) && $value[0]===0)
+ throw new TConfigurationException('theme_databind_unsupported',$type,$id,$name);
+ }
+ $this->_skins[$type][$id]=$skin[2];
+ }
+ }
+
+ 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)
+ {
+ if(is_array($value))
+ $value=$this->evaluateExpression($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);
+ }
+ else
+ throw new TConfigurationException('theme_property_readonly',get_class($control),$name);
+ }
+ else if($control->getAllowCustomAttributes())
+ $control->getAttributes()->add($name,$value);
+ else
+ throw new TConfigurationException('theme_property_undefined',get_class($control),$name);
+ }
+ else // complex property
+ $control->setSubProperty($name,$value);
+ }
+ return true;
+ }
+ else
+ return false;
+ }
+
+ public function publishFile($file)
+ {
+ return Prado::getApplication()->getService()->getAssetManager()->publishFile($file);
+ }
+
+ public function publishDirectory($directory)
+ {
+ return Prado::getApplication()->getService()->getAssetManager()->publishDirectory($directory);
+ }
+
+ public function getAsset($assetName)
+ {
+ $assetFile=$this->_themePath.'/'.self::ASSET_PATH.'/'.$assetName;
+ if(is_file($assetFile))
+ return $this->publishFile($assetFile);
+ else if(is_dir($assetFile))
+ return $this->publishDirectory($assetFile);
+ else
+ return '';
+ }
+
+}
+
+
+?>
\ No newline at end of file |