From bdd139266bbd71b828394a1500551254fc42a819 Mon Sep 17 00:00:00 2001
From: xue <>
Date: Sat, 3 Dec 2005 04:45:44 +0000
Subject:
---
.gitattributes | 8 +-
framework/Web/Services/TPageService.php | 8 +-
framework/Web/TAssetManager.php | 221 --------
framework/Web/THiddenFieldPageStatePersister.php | 80 ---
framework/Web/TTemplateManager.php | 630 ---------------------
framework/Web/TThemeManager.php | 351 ------------
framework/Web/UI/TAssetManager.php | 221 ++++++++
.../Web/UI/THiddenFieldPageStatePersister.php | 2 +-
framework/Web/UI/TPageStatePersister.php | 22 -
framework/Web/UI/TTemplateManager.php | 630 +++++++++++++++++++++
framework/Web/UI/TThemeManager.php | 351 ++++++++++++
11 files changed, 1210 insertions(+), 1314 deletions(-)
delete mode 100644 framework/Web/TAssetManager.php
delete mode 100644 framework/Web/THiddenFieldPageStatePersister.php
delete mode 100644 framework/Web/TTemplateManager.php
delete mode 100644 framework/Web/TThemeManager.php
create mode 100644 framework/Web/UI/TAssetManager.php
delete mode 100644 framework/Web/UI/TPageStatePersister.php
create mode 100644 framework/Web/UI/TTemplateManager.php
create mode 100644 framework/Web/UI/TThemeManager.php
diff --git a/.gitattributes b/.gitattributes
index 90c81e47..1ff1ed0f 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -70,23 +70,21 @@ framework/TComponent.php -text
framework/TODO.txt -text
framework/Web/Javascripts/WebForms.js -text
framework/Web/Services/TPageService.php -text
-framework/Web/TAssetManager.php -text
-framework/Web/THiddenFieldPageStatePersister.php -text
framework/Web/THttpRequest.php -text
framework/Web/THttpResponse.php -text
framework/Web/THttpSession.php -text
framework/Web/THttpUtility.php -text
-framework/Web/TTemplateManager.php -text
-framework/Web/TThemeManager.php -text
+framework/Web/UI/TAssetManager.php -text
framework/Web/UI/TClientScriptManager.php -text
framework/Web/UI/TControl.php -text
framework/Web/UI/TForm.php -text
framework/Web/UI/THiddenFieldPageStatePersister.php -text
framework/Web/UI/THtmlWriter.php -text
framework/Web/UI/TPage.php -text
-framework/Web/UI/TPageStatePersister.php -text
framework/Web/UI/TPostBackOptions.php -text
framework/Web/UI/TTemplateControl.php -text
+framework/Web/UI/TTemplateManager.php -text
+framework/Web/UI/TThemeManager.php -text
framework/Web/UI/WebControls/TButton.php -text
framework/Web/UI/WebControls/TCheckBox.php -text
framework/Web/UI/WebControls/TContent.php -text
diff --git a/framework/Web/Services/TPageService.php b/framework/Web/Services/TPageService.php
index 078f8706..a95235c2 100644
--- a/framework/Web/Services/TPageService.php
+++ b/framework/Web/Services/TPageService.php
@@ -14,10 +14,10 @@
* Include classes to be used by page service
*/
Prado::using('System.Web.UI.TPage');
-Prado::using('System.Web.TTemplateManager');
-Prado::using('System.Web.TThemeManager');
-Prado::using('System.Web.TAssetManager');
-Prado::using('System.Web.THiddenFieldPageStatePersister');
+Prado::using('System.Web.UI.TTemplateManager');
+Prado::using('System.Web.UI.TThemeManager');
+Prado::using('System.Web.UI.TAssetManager');
+Prado::using('System.Web.UI.THiddenFieldPageStatePersister');
/**
* TPageService class.
diff --git a/framework/Web/TAssetManager.php b/framework/Web/TAssetManager.php
deleted file mode 100644
index e2b9f9a8..00000000
--- a/framework/Web/TAssetManager.php
+++ /dev/null
@@ -1,221 +0,0 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Revision: $ $Date: $
- * @package System.Web
- */
-
-/**
- * TAssetManager class
- *
- * TAssetManager provides a scheme to allow web clients visiting
- * private files that are normally web-inaccessible.
- *
- * TAssetManager will copy the file to be published into a web-accessible
- * directory. The default base directory for storing the file is "assets", which
- * should be under the application directory. This can be changed by setting
- * the {@link setBasePath BasePath} property together with the
- * {@link setBaseUrl BaseUrl} property that refers to the URL for accessing the base path.
- *
- * By default, TAssetManager will not publish a file or directory if it already
- * exists in the publishing directory and has an older modification time.
- * If the application mode is set as 'Performance', the modification time check
- * will be skipped. You can explicitly require a modification time check
- * with the function {@link publishFilePath}. This is usually
- * very useful during development.
- *
- * TAssetManager may be configured in application configuration file within
- * page service element as follows,
- *
- * where {@link getBasePath BasePath} and {@link getBaseUrl BaseUrl} are
- * configurable properties of TAssetManager. Make sure that BasePath is a namespace
- * pointing to a valid directory writable by the Web server process.
- *
- * @author Qiang Xue
- * @version $Revision: $ $Date: $
- * @package System.Web
- * @since 3.0
- */
-class TAssetManager extends TComponent implements IModule
-{
- /**
- * Default web accessible base path for storing private files
- */
- const DEFAULT_BASEPATH='assets';
- /**
- * @var string base web accessible path for storing private files
- */
- private $_basePath=null;
- /**
- * @var string base URL for accessing the publishing directory.
- */
- private $_baseUrl=null;
- /**
- * @var string module ID
- */
- private $_id='asset';
- /**
- * @var boolean whether to use timestamp checking to ensure files are published with up-to-date versions.
- */
- private $_checkTimestamp=false;
- /**
- * @var TApplication application instance
- */
- private $_application;
- /**
- * @var array published assets
- */
- private $_published=array();
-
- /**
- * Initializes the module.
- * This method is required by IModule and is invoked by application.
- * @param TApplication application
- * @param TXmlElement module configuration
- */
- public function init($application,$config)
- {
- $this->_application=$application;
- if($this->_basePath===null)
- $this->_basePath=dirname($application->getRequest()->getPhysicalApplicationPath()).'/'.self::DEFAULT_BASEPATH;
- if(!is_writable($this->_basePath) || !is_dir($this->_basePath))
- throw new TConfigurationException('assetmanager_basepath_invalid',$this->_basePath);
- if($this->_baseUrl===null)
- $this->_baseUrl=dirname($application->getRequest()->getApplicationPath()).'/'.self::DEFAULT_BASEPATH;
- $application->getService()->setAssetManager($this);
- }
-
- /**
- * @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;
- }
-
- /**
- * @return string the root directory storing published asset files
- */
- public function getBasePath()
- {
- return $this->_basePath;
- }
-
- /**
- * Sets the root directory storing published asset files.
- * The directory must be in namespace format.
- * @param string the root directory storing published asset files
- * @throws TInvalidOperationException if the service is initialized already
- */
- public function setBasePath($value)
- {
- if($this->_initialized)
- throw new TInvalidOperationException('assetmanager_basepath_unchangeable');
- else
- {
- $this->_basePath=Prado::getPathOfAlias($value);
- if($this->_basePath===null || !is_dir($this->_basePath) || !is_writable($this->_basePath))
- throw new TInvalidDataValueException('assetmanage_basepath_invalid',$value);
- }
- }
-
- /**
- * @return string the base url that the published asset files can be accessed
- */
- public function getBaseUrl()
- {
- return $this->_baseUrl;
- }
-
- /**
- * @param string the base url that the published asset files can be accessed
- * @throws TInvalidOperationException if the service is initialized already
- */
- public function setBaseUrl($value)
- {
- if($this->_initialized)
- throw new TInvalidOperationException('assetmanager_baseurl_unchangeable');
- else
- $this->_baseUrl=$value;
- }
-
- /**
- * Publishes a file or a directory (recursively).
- * This method will copy the content in a directory (recursively) to
- * a web accessible directory and returns the URL for the directory.
- * @param string the path to be published
- * @param boolean whether to use file modify time to ensure every published file is latest
- * @return string an absolute URL to the published directory
- */
- public function publishFilePath($path,$checkTimestamp=false)
- {
- if(isset($this->_published[$path]))
- return $this->_published[$path];
- else if(($fullpath=realpath($path))===false)
- return '';
- else if(is_file($fullpath))
- {
- $dir=md5(dirname($fullpath));
- $file=$this->_basePath.'/'.$dir.'/'.basename($fullpath);
- if(!is_file($file) || $checkTimestamp || $this->_application->getMode()!=='Performance')
- {
- if(!is_dir($this->_basePath.'/'.$dir))
- @mkdir($this->_basePath.'/'.$dir);
- if(!is_file($file) || @filemtime($file)<@filemtime($fullpath))
- @copy($fullpath,$file);
- }
- $this->_published[$path]=$this->_baseUrl.'/'.$dir.'/'.basename($fullpath);
- return $this->_published[$path];
- }
- else
- {
- $dir=md5($fullpath);
- if(!is_dir($this->_basePath.'/'.$dir) || $checkTimestamp || $this->_application->getMode()!=='Performance')
- $this->copyDirectory($fullpath,$this->_basePath.'/'.$dir);
- $this->_published[$path]=$this->_baseUrl.'/'.$dir;
- return $this->_published[$path];
- }
- }
-
- /**
- * Copies a directory recursively as another.
- * If the destination directory does not exist, it will be created.
- * File modification time is used to ensure the copied files are latest.
- * @param string the source directory
- * @param string the destination directory
- */
- protected function copyDirectory($src,$dst)
- {
- if(!is_dir($dst))
- @mkdir($dst);
- $folder=@opendir($src);
- while($file=@readdir($folder))
- {
- if($file==='.' || $file==='..')
- continue;
- else if(is_file($src.'/'.$file))
- {
- if(@filemtime($dst.'/'.$file)<@filemtime($src.'/'.$file))
- @copy($src.'/'.$file,$dst.'/'.$file);
- }
- else
- $this->copyDirectory($src.'/'.$file,$dst.'/'.$file);
- }
- closedir($folder);
- }
-}
-
-?>
\ No newline at end of file
diff --git a/framework/Web/THiddenFieldPageStatePersister.php b/framework/Web/THiddenFieldPageStatePersister.php
deleted file mode 100644
index 3d305668..00000000
--- a/framework/Web/THiddenFieldPageStatePersister.php
+++ /dev/null
@@ -1,80 +0,0 @@
-_application=$application;
- }
-
- /**
- * @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 save($state)
- {
- $data=Prado::serialize($state);
- $hmac=$this->computeHMAC($data,$this->getKey());
- if(function_exists('gzuncompress') && function_exists('gzcompress'))
- $data=gzcompress($hmac.$data);
- else
- $data=$hmac.$data;
- $this->_application->getService()->getRequestedPage()->saveStateField($data);
- }
-
- public function load()
- {
- $str=$this->_application->getService()->getRequestedPage()->loadStateField();
- if($str==='')
- return null;
- if(function_exists('gzuncompress') && function_exists('gzcompress'))
- $data=gzuncompress($str);
- else
- $data=$str;
- if($data!==false && strlen($data)>32)
- {
- $hmac=substr($data,0,32);
- $state=substr($data,32);
- if($hmac===$this->computeHMAC($state,$this->getKey()))
- return Prado::unserialize($state);
- }
- throw new Exception('viewstate data is corrupted.');
- }
-
- private function getKey()
- {
- return 'abcdefe';
- }
-
- private function computeHMAC($data,$key)
- {
- if (strlen($key) > 64)
- $key = pack('H32', md5($key));
- else if (strlen($key) < 64)
- $key = str_pad($key, 64, "\0");
- return md5((str_repeat("\x5c", 64) ^ substr($key, 0, 64)) . pack('H32', md5((str_repeat("\x36", 64) ^ substr($key, 0, 64)) . $data)));
- }
-}
-
-?>
\ No newline at end of file
diff --git a/framework/Web/TTemplateManager.php b/framework/Web/TTemplateManager.php
deleted file mode 100644
index 1f3044a2..00000000
--- a/framework/Web/TTemplateManager.php
+++ /dev/null
@@ -1,630 +0,0 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Revision: $ $Date: $
- * @package System.Web.UI
- */
-
-/**
- * TTemplateManager class
- *
- * TTemplateManager manages the loading and parsing of control templates.
- *
- * Given a class name, TTemplateManager tries to locate the corresponding template
- * file under the directory containing the class file. The name of the template file
- * is the class name with the extension '.tpl'.
- *
- * By default, TTemplateManager is registered with {@link TPageService} as the
- * template manager module that can be accessed via {@link TPageService::getTemplateManager()}.
- *
- * @author Qiang Xue
- * @version $Revision: $ $Date: $
- * @package System.Web.UI
- * @since 3.0
- */
-class TTemplateManager extends TComponent implements IModule
-{
- /**
- * Template file extension
- */
- const TEMPLATE_FILE_EXT='.tpl';
- /**
- * Prefix of the cache variable name for storing parsed templates
- */
- const TEMPLATE_CACHE_PREFIX='prado:template:';
- /**
- * @var TApplication application instance
- */
- private $_application;
- /**
- * @var string module ID
- */
- private $_id;
-
- /**
- * Initializes the module.
- * This method is required by IModule and is invoked by application.
- * It starts output buffer if it is enabled.
- * @param TApplication application
- * @param TXmlElement module configuration
- */
- public function init($application,$config)
- {
- $this->_application=$application;
- $application->getService()->setTemplateManager($this);
- }
-
- /**
- * @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;
- }
-
- /**
- * Loads the template corresponding to the specified class name.
- * @return ITemplate template for the class name, null if template doesn't exist.
- */
- public function getTemplateByClassName($className)
- {
- $class=new ReflectionClass($className);
- $tplFile=dirname($class->getFileName()).'/'.$className.self::TEMPLATE_FILE_EXT;
- return $this->getTemplateByFileName($tplFile);
- }
-
- /**
- * Loads the template from the specified file.
- * @return ITemplate template parsed from the specified file, null if the file doesn't exist.
- */
- public function getTemplateByFileName($fileName)
- {
- if(($fileName=realpath($fileName))!==false && is_file($fileName))
- {
- if(($cache=$this->_application->getCache())===null)
- return new TTemplate(file_get_contents($fileName),dirname($fileName),$fileName);
- else
- {
- $array=$cache->get(self::TEMPLATE_CACHE_PREFIX.$fileName);
- if(is_array($array))
- {
- list($template,$timestamp)=$array;
- if(filemtime($fileName)<$timestamp)
- return $template;
- }
- $template=new TTemplate(file_get_contents($fileName),dirname($fileName),$fileName);
- $cache->set(self::TEMPLATE_CACHE_PREFIX.$fileName,array($template,time()));
- return $template;
- }
- }
- else
- return null;
- }
-}
-
-/**
- * TTemplate implements PRADO template parsing logic.
- * A TTemplate object represents a parsed PRADO control template.
- * It can instantiate the template as child controls of a specified control.
- * The template format is like HTML, with the following special tags introduced,
- * - component tags: a component tag represents the configuration of a component.
- * The tag name is in the format of com:ComponentType, where ComponentType is the component
- * class name. Component tags must be well-formed. Attributes of the component tag
- * are treated as either property initial values, event handler attachment, or regular
- * tag attributes.
- * - property tags: property tags are used to set large block of attribute values.
- * The property tag name is in the format of prop:AttributeName, where AttributeName
- * can be a property name, an event name or a regular tag attribute name.
- * - directive: directive specifies the property values for the template owner.
- * It is in the format of <% property name-value pairs %>
- * - expressions: expressions are shorthand of {@link TExpression} and {@link TStatements}
- * controls. They are in the formate of <= PHP expression > and < PHP statements >
- * - comments: There are two kinds of comments, regular HTML comments and special template comments.
- * The former is in the format of <!-- comments -->, which will be treated as text strings.
- * The latter is in the format of <%* comments %>, which will be stripped out.
- *
- * Tags other than the above are not required to be well-formed.
- *
- * A TTemplate object represents a parsed PRADO template. To instantiate the template
- * for a particular control, call {@link instantiateIn($control)}, which
- * will create and intialize all components specified in the template and
- * set their parent as the control.
- *
- * @author Qiang Xue
- * @version $Revision: $ $Date: $
- * @package System.Web.UI
- * @since 3.0
- */
-class TTemplate extends TComponent implements ITemplate
-{
- /**
- * '' - template comments
- * '' - HTML comments
- * '<\/?com:([\w\.]+)((?:\s*[\w\.]+=\'.*?\'|\s*[\w\.]+=".*?"|\s*[\w\.]+=<%.*?%>)*)\s*\/?>' - component tags
- * '<\/?prop:([\w\.]+)\s*>' - property tags
- * '<%@\s*(\w+)((?:\s*[\w\.]+=\'.*?\'|\s*[\w\.]+=".*?")*)\s*%>' - directives
- * '<%=?(.*?)%>' - expressions
- */
- const REGEX_RULES='/||<\/?com:([\w\.]+)((?:\s*[\w\.]+=\'.*?\'|\s*[\w\.]+=".*?"|\s*[\w\.]+=<%.*?%>)*)\s*\/?>|<\/?prop:([\w\.]+)\s*>|<%@\s*((?:\s*[\w\.]+=\'.*?\'|\s*[\w\.]+=".*?")*)\s*%>|<%=?(.*?)%>/msS';
-
- /**
- * @var array list of component tags and strings
- */
- private $_tpl=array();
- /**
- * @var array list of directive settings
- */
- private $_directive=array();
- /**
- * @var string context path
- */
- private $_contextPath;
- /**
- * @var string template file path (if available)
- */
- private $_tplFile=null;
-
- /**
- * Constructor.
- * The template will be parsed after construction.
- * @param string the template string
- * @param string the template context directory
- * @param string the template file, null if no file
- */
- public function __construct($template,$contextPath,$tplFile=null)
- {
- $this->_contextPath=$contextPath;
- $this->_tplFile=$tplFile;
- $this->parse($template);
- }
-
- /**
- * @return array name-value pairs declared in the directive
- */
- public function getDirective()
- {
- return $this->_directive;
- }
-
- /**
- * @return array the parsed template
- */
- public function &getItems()
- {
- return $this->_tpl;
- }
-
- /**
- * Instantiates the template.
- * Content in the template will be instantiated as components and text strings
- * and passed to the specified parent control.
- * @param TControl the parent control
- * @throws TTemplateRuntimeException if an error is encountered during the instantiation.
- */
- public function instantiateIn($tplControl)
- {
- $page=$tplControl->getPage();
- $assetManager=$page->getService()->getAssetManager();
- $controls=array();
- foreach($this->_tpl as $key=>$object)
- {
- if(isset($object[2])) // component
- {
- if(strpos($object[1],'.')===false)
- {
- if(class_exists($object[1],false))
- $component=new $object[1];
- else
- {
- include_once($object[1].Prado::CLASS_FILE_EXT);
- if(class_exists($object[1],false))
- $component=new $object[1];
- else
- throw new TTemplateRuntimeException('template_component_unknown',$object[1]);
- }
- }
- else
- $component=Prado::createComponent($object[1]);
- if($component instanceof TControl)
- {
- $controls[$key]=$component;
- $component->setTemplateControl($tplControl);
- if(isset($object[2]['id']))
- $tplControl->registerObject($object[2]['id'],$component);
- if(isset($object[2]['skinid']))
- {
- $component->setSkinID($object[2]['skinid']);
- unset($object[2]['skinid']);
- }
- $component->applyStyleSheetSkin($page);
- // apply attributes
- foreach($object[2] as $name=>$value)
- {
- if($component->hasEvent($name)) // is an event
- {
- if(is_string($value))
- {
- if(strpos($value,'.')===false)
- $component->attachEventHandler($name,array($component,'TemplateControl.'.$value));
- else
- $component->attachEventHandler($name,array($component,$value));
- }
- else
- throw new TTemplateRuntimeException('template_event_invalid',get_class($component),$name);
- }
- else if(strpos($name,'.')===false) // is simple property or custom attribute
- {
- if($component->hasProperty($name))
- {
- if($component->canSetProperty($name))
- {
- $setter='set'.$name;
- if(is_string($value))
- $component->$setter($value);
- else if($value[0]===0)
- $component->bindProperty($name,$value[1]);
- else if($value[0]===1)
- $component->$setter($component->evaluateExpression($value[1]));
- else // url
- {
- $url=$assetManager->publishFilePath($this->_contextPath.'/'.$value[1]);
- $component->$setter($url);
- }
- }
- else
- throw new TTemplateRuntimeException('template_property_readonly',get_class($component),$name);
- }
- else if($component->getAllowCustomAttributes())
- {
- if(is_array($value))
- {
- if($value[0]===1)
- $value=$component->evaluateExpression($value[1]);
- else if($value[0]===2)
- $value=$assetManager->publishFilePath($this->_contextPath.'/'.ltrim($value[1],'/'));
- else
- throw new TTemplateRuntimeException('template_attribute_unbindable',get_class($component),$name);
- }
- $component->getAttributes()->add($name,$value);
- }
- else
- throw new TTemplateRuntimeException('template_property_undefined',get_class($component),$name);
- }
- else // complex property
- {
- if(is_string($value))
- $component->setSubProperty($name,$value);
- else if($value[0]===0)
- $component->bindProperty($name,$value[1]);
- else if($value[0]===1)
- $component->setSubProperty($component->evaluateExpression($value[1]));
- else
- {
- $url=$assetManager->publishFilePath($this->_contextPath.'/'.$value[1]);
- $component->$setter($url);
- }
- }
- }
- $parent=isset($controls[$object[0]])?$controls[$object[0]]:$tplControl;
- $component->createdOnTemplate($parent);
- }
- else if($component instanceof TComponent)
- {
- if(isset($object[2]['id']))
- {
- $tplControl->registerObject($object[2]['id'],$component);
- if(!$component->hasProperty('id'))
- unset($object[2]['id']);
- }
- foreach($object[2] as $name=>$value)
- {
- if($component->hasProperty($name))
- {
- if($component->canSetProperty($name))
- {
- $setter='set'.$name;
- if(is_string($value))
- $component->$setter($value);
- else if($value[0]===1)
- $component->$setter($component->evaluateExpression($value[1]));
- else if($value[0]===2)
- {
- $url=$assetManager->publishFilePath($this->_contextPath.'/'.ltrim($value[1],'/'));
- $component->$setter($url);
- }
- else
- throw new TTemplateRuntimeException('template_property_unbindable',get_class($component),$name);
- }
- else
- throw new TTemplateRuntimeException('template_property_readonly',get_class($component),$name);
- }
- else
- throw new TTemplateRuntimeException('template_property_undefined',get_class($component),$name);
- }
- $parent=isset($controls[$object[0]])?$controls[$object[0]]:$tplControl;
- $parent->addParsedObject($component);
- }
- else
- throw new TTemplateRuntimeException('template_component_required',$object[1]);
- }
- else // string
- {
- if(isset($controls[$object[0]]))
- $controls[$object[0]]->addParsedObject($object[1]);
- else
- $tplControl->addParsedObject($object[1]);
- }
- }
- }
-
- /**
- * Parses a template string.
- *
- * This template parser recognizes five types of data:
- * regular string, well-formed component tags, well-formed property tags, directives, and expressions.
- *
- * The parsing result is returned as an array. Each array element can be of three types:
- * - a string, 0: container index; 1: string content;
- * - a component tag, 0: container index; 1: component type; 2: attributes (name=>value pairs)
- * If a directive is found in the template, it will be parsed and can be
- * retrieved via {@link getDirective}, which returns an array consisting of
- * name-value pairs in the directive.
- *
- * Note, attribute names are treated as case-insensitive and will be turned into lower cases.
- * Component and directive types are case-sensitive.
- * Container index is the index to the array element that stores the container object.
- * If an object has no container, its container index is -1.
- *
- * @param string the template string
- * @throws TTemplateParsingException if a parsing error is encountered
- */
- protected function parse($input)
- {
- $tpl=&$this->_tpl;
- $n=preg_match_all(self::REGEX_RULES,$input,$matches,PREG_SET_ORDER|PREG_OFFSET_CAPTURE);
- $expectPropEnd=false;
- $textStart=0;
- $stack=array();
- $container=-1;
- $c=0;
- for($i=0;$i<$n;++$i)
- {
- $match=&$matches[$i];
- $str=$match[0][0];
- $matchStart=$match[0][1];
- $matchEnd=$matchStart+strlen($str)-1;
- if(strpos($str,'$textStart)
- $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
- $textStart=$matchEnd+1;
- $type=$match[1][0];
- $attributes=$this->parseAttributes($match[2][0]);
- $tpl[$c++]=array($container,$type,$attributes);
- if($str[strlen($str)-2]!=='/') // open tag
- {
- array_push($stack,$type);
- $container=$c-1;
- }
- }
- else if(strpos($str,'$textStart)
- $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
- $textStart=$matchEnd+1;
- $type=$match[1][0];
-
- if(empty($stack))
- {
- $line=count(explode("\n",substr($input,0,$matchEnd+1)));
- if($this->_tplFile===null)
- throw new TTemplateParsingException('template_closingtag_unexpected',"Line $line","");
- else
- throw new TTemplateParsingException('template_closingtag_unexpected',"{$this->_tplFile} (Line $line)","");
- }
-
- $name=array_pop($stack);
- if($name!==$type)
- {
- if($name[0]==='@')
- $tag='';
- else
- $tag='';
- $line=count(explode("\n",substr($input,0,$matchEnd+1)));
- if($this->_tplFile===null)
- throw new TTemplateParsingException('template_closingtag_expected',"Line $line",$tag);
- else
- throw new TTemplateParsingException('template_closingtag_expected',"{$this->_tplFile} (Line $line)",$tag);
- }
- $container=$tpl[$container][0];
- }
- else if(strpos($str,'<%@')===0) // directive
- {
- if($expectPropEnd)
- continue;
- if($matchStart>$textStart)
- $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
- $textStart=$matchEnd+1;
- if(isset($tpl[0]))
- {
- $line=count(explode("\n",substr($input,0,$matchEnd+1)));
- if($this->_tplFile===null)
- throw new TTemplateParsingException('template_directive_nonunique',"Line $line");
- else
- throw new TTemplateParsingException('template_directive_nonunique',"{$this->_tplFile} (Line $line)");
- }
- $this->_directive=$this->parseAttributes($match[4][0]);
- }
- else if(strpos($str,'<%')===0) // expression
- {
- if($expectPropEnd)
- continue;
- if($matchStart>$textStart)
- $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
- $textStart=$matchEnd+1;
- if($str[2]==='=')
- $tpl[$c++]=array($container,'TExpression',array('Expression'=>$match[5][0]));
- else
- $tpl[$c++]=array($container,'TStatements',array('Statements'=>$match[5][0]));
- }
- else if(strpos($str,'$textStart)
- $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
- $textStart=$matchEnd+1;
- $expectPropEnd=true;
- }
- }
- else if(strpos($str,'_tplFile===null)
- throw new TTemplateParsingException('template_closingtag_unexpected',"Line $line","");
- else
- throw new TTemplateParsingException('template_closingtag_unexpected',"{$this->_tplFile} (Line $line)","");
- }
- $name=array_pop($stack);
- if($name!=='@'.$prop)
- {
- if($name[0]==='@')
- $tag='';
- else
- $tag='';
- $line=count(explode("\n",substr($input,0,$matchEnd+1)));
- if($this->_tplFile===null)
- throw new TTemplateParsingException('template_closingtag_expected',"Line $line",$tag);
- else
- throw new TTemplateParsingException('template_closingtag_expected',"{$this->_tplFile} (Line $line)",$tag);
- }
- if(($last=count($stack))<1 || $stack[$last-1][0]!=='@')
- {
- if($matchStart>$textStart && $container>=0)
- {
- $value=substr($input,$textStart,$matchStart-$textStart);
- if(preg_match('/^<%.*?%>$/msS',$value))
- {
- if($value[2]==='#') // databind
- $tpl[$container][2][$prop]=array(0,substr($value,3,strlen($value)-5));
- else if($value[2]==='=') // a dynamic initialization
- $tpl[$container][2][$prop]=array(1,substr($value,3,strlen($value)-5));
- else
- $tpl[$container][2][$prop]=$value;
- }
- else
- $tpl[$container][2][$prop]=$value;
- $textStart=$matchEnd+1;
- }
- $expectPropEnd=false;
- }
- }
- else if(strpos($str,'' - HTML comments
+ * '<\/?com:([\w\.]+)((?:\s*[\w\.]+=\'.*?\'|\s*[\w\.]+=".*?"|\s*[\w\.]+=<%.*?%>)*)\s*\/?>' - component tags
+ * '<\/?prop:([\w\.]+)\s*>' - property tags
+ * '<%@\s*(\w+)((?:\s*[\w\.]+=\'.*?\'|\s*[\w\.]+=".*?")*)\s*%>' - directives
+ * '<%=?(.*?)%>' - expressions
+ */
+ const REGEX_RULES='/||<\/?com:([\w\.]+)((?:\s*[\w\.]+=\'.*?\'|\s*[\w\.]+=".*?"|\s*[\w\.]+=<%.*?%>)*)\s*\/?>|<\/?prop:([\w\.]+)\s*>|<%@\s*((?:\s*[\w\.]+=\'.*?\'|\s*[\w\.]+=".*?")*)\s*%>|<%=?(.*?)%>/msS';
+
+ /**
+ * @var array list of component tags and strings
+ */
+ private $_tpl=array();
+ /**
+ * @var array list of directive settings
+ */
+ private $_directive=array();
+ /**
+ * @var string context path
+ */
+ private $_contextPath;
+ /**
+ * @var string template file path (if available)
+ */
+ private $_tplFile=null;
+
+ /**
+ * Constructor.
+ * The template will be parsed after construction.
+ * @param string the template string
+ * @param string the template context directory
+ * @param string the template file, null if no file
+ */
+ public function __construct($template,$contextPath,$tplFile=null)
+ {
+ $this->_contextPath=$contextPath;
+ $this->_tplFile=$tplFile;
+ $this->parse($template);
+ }
+
+ /**
+ * @return array name-value pairs declared in the directive
+ */
+ public function getDirective()
+ {
+ return $this->_directive;
+ }
+
+ /**
+ * @return array the parsed template
+ */
+ public function &getItems()
+ {
+ return $this->_tpl;
+ }
+
+ /**
+ * Instantiates the template.
+ * Content in the template will be instantiated as components and text strings
+ * and passed to the specified parent control.
+ * @param TControl the parent control
+ * @throws TTemplateRuntimeException if an error is encountered during the instantiation.
+ */
+ public function instantiateIn($tplControl)
+ {
+ $page=$tplControl->getPage();
+ $assetManager=$page->getService()->getAssetManager();
+ $controls=array();
+ foreach($this->_tpl as $key=>$object)
+ {
+ if(isset($object[2])) // component
+ {
+ if(strpos($object[1],'.')===false)
+ {
+ if(class_exists($object[1],false))
+ $component=new $object[1];
+ else
+ {
+ include_once($object[1].Prado::CLASS_FILE_EXT);
+ if(class_exists($object[1],false))
+ $component=new $object[1];
+ else
+ throw new TTemplateRuntimeException('template_component_unknown',$object[1]);
+ }
+ }
+ else
+ $component=Prado::createComponent($object[1]);
+ if($component instanceof TControl)
+ {
+ $controls[$key]=$component;
+ $component->setTemplateControl($tplControl);
+ if(isset($object[2]['id']))
+ $tplControl->registerObject($object[2]['id'],$component);
+ if(isset($object[2]['skinid']))
+ {
+ $component->setSkinID($object[2]['skinid']);
+ unset($object[2]['skinid']);
+ }
+ $component->applyStyleSheetSkin($page);
+ // apply attributes
+ foreach($object[2] as $name=>$value)
+ {
+ if($component->hasEvent($name)) // is an event
+ {
+ if(is_string($value))
+ {
+ if(strpos($value,'.')===false)
+ $component->attachEventHandler($name,array($component,'TemplateControl.'.$value));
+ else
+ $component->attachEventHandler($name,array($component,$value));
+ }
+ else
+ throw new TTemplateRuntimeException('template_event_invalid',get_class($component),$name);
+ }
+ else if(strpos($name,'.')===false) // is simple property or custom attribute
+ {
+ if($component->hasProperty($name))
+ {
+ if($component->canSetProperty($name))
+ {
+ $setter='set'.$name;
+ if(is_string($value))
+ $component->$setter($value);
+ else if($value[0]===0)
+ $component->bindProperty($name,$value[1]);
+ else if($value[0]===1)
+ $component->$setter($component->evaluateExpression($value[1]));
+ else // url
+ {
+ $url=$assetManager->publishFilePath($this->_contextPath.'/'.$value[1]);
+ $component->$setter($url);
+ }
+ }
+ else
+ throw new TTemplateRuntimeException('template_property_readonly',get_class($component),$name);
+ }
+ else if($component->getAllowCustomAttributes())
+ {
+ if(is_array($value))
+ {
+ if($value[0]===1)
+ $value=$component->evaluateExpression($value[1]);
+ else if($value[0]===2)
+ $value=$assetManager->publishFilePath($this->_contextPath.'/'.ltrim($value[1],'/'));
+ else
+ throw new TTemplateRuntimeException('template_attribute_unbindable',get_class($component),$name);
+ }
+ $component->getAttributes()->add($name,$value);
+ }
+ else
+ throw new TTemplateRuntimeException('template_property_undefined',get_class($component),$name);
+ }
+ else // complex property
+ {
+ if(is_string($value))
+ $component->setSubProperty($name,$value);
+ else if($value[0]===0)
+ $component->bindProperty($name,$value[1]);
+ else if($value[0]===1)
+ $component->setSubProperty($component->evaluateExpression($value[1]));
+ else
+ {
+ $url=$assetManager->publishFilePath($this->_contextPath.'/'.$value[1]);
+ $component->$setter($url);
+ }
+ }
+ }
+ $parent=isset($controls[$object[0]])?$controls[$object[0]]:$tplControl;
+ $component->createdOnTemplate($parent);
+ }
+ else if($component instanceof TComponent)
+ {
+ if(isset($object[2]['id']))
+ {
+ $tplControl->registerObject($object[2]['id'],$component);
+ if(!$component->hasProperty('id'))
+ unset($object[2]['id']);
+ }
+ foreach($object[2] as $name=>$value)
+ {
+ if($component->hasProperty($name))
+ {
+ if($component->canSetProperty($name))
+ {
+ $setter='set'.$name;
+ if(is_string($value))
+ $component->$setter($value);
+ else if($value[0]===1)
+ $component->$setter($component->evaluateExpression($value[1]));
+ else if($value[0]===2)
+ {
+ $url=$assetManager->publishFilePath($this->_contextPath.'/'.ltrim($value[1],'/'));
+ $component->$setter($url);
+ }
+ else
+ throw new TTemplateRuntimeException('template_property_unbindable',get_class($component),$name);
+ }
+ else
+ throw new TTemplateRuntimeException('template_property_readonly',get_class($component),$name);
+ }
+ else
+ throw new TTemplateRuntimeException('template_property_undefined',get_class($component),$name);
+ }
+ $parent=isset($controls[$object[0]])?$controls[$object[0]]:$tplControl;
+ $parent->addParsedObject($component);
+ }
+ else
+ throw new TTemplateRuntimeException('template_component_required',$object[1]);
+ }
+ else // string
+ {
+ if(isset($controls[$object[0]]))
+ $controls[$object[0]]->addParsedObject($object[1]);
+ else
+ $tplControl->addParsedObject($object[1]);
+ }
+ }
+ }
+
+ /**
+ * Parses a template string.
+ *
+ * This template parser recognizes five types of data:
+ * regular string, well-formed component tags, well-formed property tags, directives, and expressions.
+ *
+ * The parsing result is returned as an array. Each array element can be of three types:
+ * - a string, 0: container index; 1: string content;
+ * - a component tag, 0: container index; 1: component type; 2: attributes (name=>value pairs)
+ * If a directive is found in the template, it will be parsed and can be
+ * retrieved via {@link getDirective}, which returns an array consisting of
+ * name-value pairs in the directive.
+ *
+ * Note, attribute names are treated as case-insensitive and will be turned into lower cases.
+ * Component and directive types are case-sensitive.
+ * Container index is the index to the array element that stores the container object.
+ * If an object has no container, its container index is -1.
+ *
+ * @param string the template string
+ * @throws TTemplateParsingException if a parsing error is encountered
+ */
+ protected function parse($input)
+ {
+ $tpl=&$this->_tpl;
+ $n=preg_match_all(self::REGEX_RULES,$input,$matches,PREG_SET_ORDER|PREG_OFFSET_CAPTURE);
+ $expectPropEnd=false;
+ $textStart=0;
+ $stack=array();
+ $container=-1;
+ $c=0;
+ for($i=0;$i<$n;++$i)
+ {
+ $match=&$matches[$i];
+ $str=$match[0][0];
+ $matchStart=$match[0][1];
+ $matchEnd=$matchStart+strlen($str)-1;
+ if(strpos($str,'$textStart)
+ $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
+ $textStart=$matchEnd+1;
+ $type=$match[1][0];
+ $attributes=$this->parseAttributes($match[2][0]);
+ $tpl[$c++]=array($container,$type,$attributes);
+ if($str[strlen($str)-2]!=='/') // open tag
+ {
+ array_push($stack,$type);
+ $container=$c-1;
+ }
+ }
+ else if(strpos($str,'$textStart)
+ $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
+ $textStart=$matchEnd+1;
+ $type=$match[1][0];
+
+ if(empty($stack))
+ {
+ $line=count(explode("\n",substr($input,0,$matchEnd+1)));
+ if($this->_tplFile===null)
+ throw new TTemplateParsingException('template_closingtag_unexpected',"Line $line","");
+ else
+ throw new TTemplateParsingException('template_closingtag_unexpected',"{$this->_tplFile} (Line $line)","");
+ }
+
+ $name=array_pop($stack);
+ if($name!==$type)
+ {
+ if($name[0]==='@')
+ $tag='';
+ else
+ $tag='';
+ $line=count(explode("\n",substr($input,0,$matchEnd+1)));
+ if($this->_tplFile===null)
+ throw new TTemplateParsingException('template_closingtag_expected',"Line $line",$tag);
+ else
+ throw new TTemplateParsingException('template_closingtag_expected',"{$this->_tplFile} (Line $line)",$tag);
+ }
+ $container=$tpl[$container][0];
+ }
+ else if(strpos($str,'<%@')===0) // directive
+ {
+ if($expectPropEnd)
+ continue;
+ if($matchStart>$textStart)
+ $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
+ $textStart=$matchEnd+1;
+ if(isset($tpl[0]))
+ {
+ $line=count(explode("\n",substr($input,0,$matchEnd+1)));
+ if($this->_tplFile===null)
+ throw new TTemplateParsingException('template_directive_nonunique',"Line $line");
+ else
+ throw new TTemplateParsingException('template_directive_nonunique',"{$this->_tplFile} (Line $line)");
+ }
+ $this->_directive=$this->parseAttributes($match[4][0]);
+ }
+ else if(strpos($str,'<%')===0) // expression
+ {
+ if($expectPropEnd)
+ continue;
+ if($matchStart>$textStart)
+ $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
+ $textStart=$matchEnd+1;
+ if($str[2]==='=')
+ $tpl[$c++]=array($container,'TExpression',array('Expression'=>$match[5][0]));
+ else
+ $tpl[$c++]=array($container,'TStatements',array('Statements'=>$match[5][0]));
+ }
+ else if(strpos($str,'$textStart)
+ $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
+ $textStart=$matchEnd+1;
+ $expectPropEnd=true;
+ }
+ }
+ else if(strpos($str,'_tplFile===null)
+ throw new TTemplateParsingException('template_closingtag_unexpected',"Line $line","");
+ else
+ throw new TTemplateParsingException('template_closingtag_unexpected',"{$this->_tplFile} (Line $line)","");
+ }
+ $name=array_pop($stack);
+ if($name!=='@'.$prop)
+ {
+ if($name[0]==='@')
+ $tag='';
+ else
+ $tag='';
+ $line=count(explode("\n",substr($input,0,$matchEnd+1)));
+ if($this->_tplFile===null)
+ throw new TTemplateParsingException('template_closingtag_expected',"Line $line",$tag);
+ else
+ throw new TTemplateParsingException('template_closingtag_expected',"{$this->_tplFile} (Line $line)",$tag);
+ }
+ if(($last=count($stack))<1 || $stack[$last-1][0]!=='@')
+ {
+ if($matchStart>$textStart && $container>=0)
+ {
+ $value=substr($input,$textStart,$matchStart-$textStart);
+ if(preg_match('/^<%.*?%>$/msS',$value))
+ {
+ if($value[2]==='#') // databind
+ $tpl[$container][2][$prop]=array(0,substr($value,3,strlen($value)-5));
+ else if($value[2]==='=') // a dynamic initialization
+ $tpl[$container][2][$prop]=array(1,substr($value,3,strlen($value)-5));
+ else
+ $tpl[$container][2][$prop]=$value;
+ }
+ else
+ $tpl[$container][2][$prop]=$value;
+ $textStart=$matchEnd+1;
+ }
+ $expectPropEnd=false;
+ }
+ }
+ else if(strpos($str,'