From 55c4ac1bfe565f1ca7f537fdd8b7a201be28e581 Mon Sep 17 00:00:00 2001 From: xue <> Date: Thu, 10 Nov 2005 12:47:19 +0000 Subject: Initial import of prado framework --- framework/Web/Services/TPageService.php | 625 ++++++++++++++++++++++++++++++++ 1 file changed, 625 insertions(+) create mode 100644 framework/Web/Services/TPageService.php (limited to 'framework/Web/Services/TPageService.php') diff --git a/framework/Web/Services/TPageService.php b/framework/Web/Services/TPageService.php new file mode 100644 index 00000000..6a19273d --- /dev/null +++ b/framework/Web/Services/TPageService.php @@ -0,0 +1,625 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.Web.Services + */ + +Prado::using('System.Web.UI.TPage'); +/** + * TPageService class. + * + * TPageService implements a service that can serve user requested pages. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Services + * @since 3.0 + */ +class TPageService extends TComponent implements IService +{ + /** + * Configuration file name + */ + const CONFIG_FILE='config.xml'; + /** + * Prefix of ID used for storing parsed configuration in cache + */ + const CONFIG_CACHE_PREFIX='prado:pageservice:'; + /** + * @var string id of this service (page) + */ + private $_id; + /** + * @var string root path of pages + */ + private $_rootPath; + /** + * @var string default page + */ + private $_defaultPage=null; + /** + * @var string requested page (path) + */ + private $_pagePath; + /** + * @var string requested page type + */ + private $_pageType; + /** + * @var array list of initial page property values + */ + private $_properties; + /** + * @var integer cache expiration + */ + private $_cacheExpire=-1; + /** + * @var boolean whether service is initialized + */ + private $_initialized=false; + /** + * @var IApplication application + */ + private $_application; + + /** + * Initializes the service. + * This method is required by IService interface and is invoked by application. + * @param IApplication application + * @param TXmlElement service configuration + */ + public function init($application,$config) + { + $this->_application=$application; + + if(($rootPath=Prado::getPathOfNamespace($this->_rootPath))===null || !is_dir($rootPath)) + throw new TConfigurationException('pageservice_rootpath_invalid',$this->_rootPath); + + $this->_pagePath=$application->getRequest()->getServiceParameter(); + if(empty($this->_pagePath)) + $this->_pagePath=$this->_defaultPage; + if(empty($this->_pagePath)) + throw new THttpException('pageservice_page_required'); + + if(($cache=$application->getCache())===null) + { + $config=new TPageConfiguration; + $config->loadConfiguration($this->_pagePath,$rootPath); + } + else + { + $configCached=true; + $arr=$cache->get(self::CONFIG_CACHE_PREFIX.$this->_pagePath); + if(is_array($arr)) + { + list($config,$timestamp)=$arr; + if($this->_cacheExpire<0) + { + // check to see if cache is the latest + $paths=explode('.',$this->_pagePath); + array_pop($paths); + $configPath=$rootPath; + foreach($paths as $path) + { + if(@filemtime($configPath.'/'.self::CONFIG_FILE)>$timestamp) + { + $configCached=false; + break; + } + $configPath.='/'.$path; + } + if(@filemtime($configPath.'/'.self::CONFIG_FILE)>$timestamp) + $configCached=false; + } + } + else + $configCached=false; + if(!$configCached) + { + $config=new TPageConfiguration; + $config->loadConfiguration($this->_pagePath,$rootPath); + $cache->set(self::CONFIG_CACHE_PREFIX.$this->_pagePath,array($config,time()),$this->_cacheExpire<0?0:$this->_cacheExpire); + } + } + + $this->_pageType=$config->getPageType(); + + // set path aliases and using namespaces + foreach($config->getAliases() as $alias=>$path) + Prado::setPathAlias($alias,$path); + foreach($config->getUsings() as $using) + Prado::using($using); + + $this->_properties=$config->getProperties(); + + // load parameters + $parameters=$application->getParameters(); + foreach($config->getParameters() as $id=>$parameter) + { + if(is_string($parameter)) + $parameters->add($id,$parameter); + else + { + $component=Prado::createComponent($parameter[0]); + foreach($parameter[1] as $name=>$value) + $component->setPropertyByPath($name,$value); + $parameters->add($id,$component); + } + } + + // load modules specified in app config + foreach($config->getModules() as $id=>$moduleConfig) + { + $module=Prado::createComponent($moduleConfig[0]); + $application->setModule($id,$module); + foreach($moduleConfig[1] as $name=>$value) + $module->setPropertyByPath($name,$value); + $module->init($this->_application,$moduleConfig[2]); + } + + if(($auth=$application->getAuthManager())!==null) + $auth->getAuthorizationRules()->mergeWith($config->getRules()); + + $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; + } + + /** + * @return TTemplateManager template manager + */ + public function getTemplateManager() + { + return $this->_application->getModule('template'); + } + + /** + * @return boolean true if the pagepath is currently being requested, false otherwise + */ + public function isRequestingPage($pagePath) + { + return $this->_pagePath===$pagePath; + } + + /** + * @return integer the expiration time of the configuration saved in cache, + * -1 (default) ensures the cached configuration always catches up the latest configuration files, + * 0 means never expire, + * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. + * a number greater than 60 means a UNIX timestamp after which the value will expire. + */ + public function getCacheExpire() + { + return $this->_cacheExpire; + } + + /** + * Sets the expiration time of the configuration saved in cache. + * TPageService will try to use cache to save parsed configuration files. + * CacheExpire is used to control the caching policy. + * If you have changed this property, make sure to clean up cache first. + * @param integer the expiration time of the configuration saved in cache, + * -1 (default) ensures the cached configuration always catches up the latest configuration files, + * 0 means never expire, + * a number less or equal than 60*60*24*30 means the number of seconds that the value will remain valid. + * a number greater than 60 means a UNIX timestamp after which the value will expire. + * @throws TInvalidOperationException if the service is already initialized + */ + public function setCacheExpire($value) + { + if($this->_initialized) + throw new TInvalidOperationException('pageservice_cacheexpire_unchangeable'); + else + $this->_cacheExpire=TPropertyValue::ensureInteger($value); + } + + /** + * @return string default page path to be served if no explicit page is request + */ + public function getDefaultPage() + { + return $this->_defaultPage; + } + + /** + * @param string default page path to be served if no explicit page is request + * @throws TInvalidOperationException if the page service is initialized + */ + public function setDefaultPage($value) + { + if($this->_initialized) + throw new TInvalidOperationException('pageservice_defaultpage_unchangeable'); + else + $this->_defaultPage=$value; + } + + /** + * @return string root directory (in namespace form) storing pages + */ + public function getRootPath() + { + return $this->_rootPath; + } + + /** + * @param string root directory (in namespace form) storing pages + * @throws TInvalidOperationException if application is initialized + */ + public function setRootPath($value) + { + if($this->_initialized) + throw new TInvalidOperationException('pageservice_rootpath_unchangeable'); + else + $this->_rootPath=$value; + } + + /** + * Runs the service. + * This will create the requested page, initializes it with the property values + * specified in the configuration, and executes the page. + */ + public function run() + { + $page=null; + if(($pos=strpos($this->_pageType,'.'))===false) + { + $className=$this->_pageType; + if(!class_exists($className,false)) + { + $p=explode('.',$this->_pagePath); + array_pop($p); + array_push($p,$className); + $path=Prado::getPathOfNamespace($this->_rootPath).'/'.implode('/',$p).Prado::CLASS_FILE_EXT; + require_once($path); + } + } + else + { + $className=substr($this->_pageType,$pos+1); + if(($path=self::getPathOfNamespace($this->_pageType,Prado::CLASS_FILE_EXT))!==null) + { + if(!class_exists($className,false)) + { + require_once($path); + } + } + } + if(class_exists($className,false)) + $page=new $className($this->_properties); + else + throw new THttpException('pageservice_page_unknown',$this->_pageType); + $writer=new THtmlTextWriter($this->_application->getResponse()); + $page->run($writer); + $writer->flush(); + } + + /** + * Constructs a URL with specified page path and GET parameters. + * @param string page path + * @param array list of GET parameters, null if no GET parameters required + * @return string URL for the page and GET parameters + */ + public function constructUrl($pagePath,$getParams=null) + { + return $this->_application->getRequest()->constructUrl($this->_id,$pagePath,$getParams); + } +} + + +/** + * TPageConfiguration class + * + * TPageConfiguration represents the configuration for a page. + * The page is specified by a dot-connected path. + * Configurations along this path are merged together to be provided for the page. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Services + * @since 3.0 + */ +class TPageConfiguration extends TComponent +{ + /** + * @var array list of page initial property values + */ + private $_properties=array(); + /** + * @var string page type + */ + private $_pageType=null; + /** + * @var array list of namespaces to be used + */ + private $_usings=array(); + /** + * @var array list of path aliases + */ + private $_aliases=array(); + /** + * @var array list of module configurations + */ + private $_modules=array( + 'template'=>array('System.Web.UI.TTemplateManager',array(),null), + ); + /** + * @var array list of parameters + */ + private $_parameters=array(); + /** + * @var TAuthorizationRuleCollection list of authorization rules + */ + private $_rules=array(); + + /** + * Returns list of page initial property values. + * Each array element represents a single property with the key + * being the property name and the value the initial property value. + * @return array list of page initial property values + */ + public function getProperties() + { + return $this->_properties; + } + + /** + * @return string the requested page type + */ + public function getPageType() + { + return $this->_pageType; + } + + /** + * Returns list of path alias definitions. + * The definitions are aggregated (top-down) from configuration files along the path + * to the specified page. Each array element represents a single alias definition, + * with the key being the alias name and the value the absolute path. + * @return array list of path alias definitions + */ + public function getAliases() + { + return $this->_aliases; + } + + /** + * Returns list of namespaces to be used. + * The namespaces are aggregated (top-down) from configuration files along the path + * to the specified page. Each array element represents a single namespace usage, + * with the value being the namespace to be used. + * @return array list of namespaces to be used + */ + public function getUsings() + { + return $this->_usings; + } + + /** + * Returns list of module configurations. + * The module configurations are aggregated (top-down) from configuration files + * along the path to the specified page. Each array element represents + * a single module configuration, with the key being the module ID and + * the value the module configuration. Each module configuration is + * stored in terms of an array with the following content + * ([0]=>module type, [1]=>module properties, [2]=>complete module configuration) + * The module properties are an array of property values indexed by property names. + * The complete module configuration is a TXmlElement object representing + * the raw module configuration which may contain contents enclosed within + * module tags. + * @return array list of module configurations to be used + */ + public function getModules() + { + return $this->_modules; + } + + /** + * Returns list of parameter definitions. + * The parameter definitions are aggregated (top-down) from configuration files + * along the path to the specified page. Each array element represents + * a single parameter definition, with the key being the parameter ID and + * the value the parameter definition. A parameter definition can be either + * a string representing a string-typed parameter, or an array. + * The latter defines a component-typed parameter whose format is as follows, + * ([0]=>component type, [1]=>component properties) + * The component properties are an array of property values indexed by property names. + * @return array list of parameter definitions to be used + */ + public function getParameters() + { + return $this->_parameters; + } + + /** + * Returns list of authorization rules. + * The authorization rules are aggregated (bottom-up) from configuration files + * along the path to the specified page. + * @return TAuthorizationRuleCollection collection of authorization rules + */ + public function getRules() + { + return $this->_rules; + } + + /** + * Loads configuration for a page specified in a path format. + * @param string path to the page (dot-connected format) + * @param string root path for pages + */ + public function loadConfiguration($pagePath,$rootPath) + { + $paths=explode('.',$pagePath); + $page=array_pop($paths); + $path=$rootPath; + foreach($paths as $p) + { + $this->loadFromFile($path.'/'.TPageService::CONFIG_FILE,null); + $path.='/'.$p; + } + $this->loadFromFile($path.'/'.TPageService::CONFIG_FILE,$page); + $this->_rules=new TAuthorizationRuleCollection($this->_rules); + } + + /** + * Loads a specific config file. + * @param string config file name + * @param string page name, null if page is not required + */ + private function loadFromFile($fname,$page) + { + if(empty($fname) || !is_file($fname)) + { + if($page===null) + return; + } + $configPath=dirname($fname); + $dom=new TXmlDocument; + $dom->loadFromFile($fname); + + // paths + if(($pathsNode=$dom->getElementByTagName('paths'))!==null) + { + foreach($pathsNode->getElementsByTagName('alias') as $aliasNode) + { + if(($id=$aliasNode->getAttribute('id'))!==null && ($p=$aliasNode->getAttribute('path'))!==null) + { + $p=str_replace('\\','/',$p); + $path=realpath(preg_match('/^\\/|.:\\//',$p)?$p:$configPath.'/'.$p); + if($path===false || !is_dir($path)) + throw new TConfigurationException('pageservice_alias_path_invalid',$fname,$id,$p); + if(isset($this->_aliases[$id])) + throw new TConfigurationException('pageservice_alias_redefined',$fname,$id); + $this->_aliases[$id]=$path; + } + else + throw new TConfigurationException('pageservice_alias_element_invalid',$fname); + } + foreach($pathsNode->getElementsByTagName('using') as $usingNode) + { + if(($namespace=$usingNode->getAttribute('namespace'))!==null) + $this->_usings[]=$namespace; + else + throw new TConfigurationException('pageservice_using_element_invalid',$fname); + } + } + + // modules + if(($modulesNode=$dom->getElementByTagName('modules'))!==null) + { + foreach($modulesNode->getElementsByTagName('module') as $node) + { + $properties=$node->getAttributes(); + $type=$properties->remove('type'); + if(($id=$properties->itemAt('id'))===null) + throw new TConfigurationException('pageservice_module_element_invalid',$fname); + if(isset($this->_modules[$id])) + { + if($type===null) + { + $this->_modules[$id][1]=array_merge($this->_modules[$id][1],$properties->toArray()); + $elements=$this->_modules[$id][2]->getElements(); + foreach($node->getElements() as $element) + $elements->add($element); + } + else + throw new TConfigurationException('pageservice_module_redefined',$fname,$id); + } + else if($type===null) + throw new TConfigurationException('pageservice_module_element_invalid',$fname); + else + { + $node->setParent(null); + $this->_modules[$id]=array($type,$properties->toArray(),$node); + } + } + } + + // parameters + if(($parametersNode=$dom->getElementByTagName('parameters'))!==null) + { + foreach($parametersNode->getElementsByTagName('parameter') as $node) + { + $properties=$node->getAttributes(); + if(($id=$properties->remove('id'))===null) + throw new TConfigurationException('pageservice_parameter_element_invalid'); + if(($type=$properties->remove('type'))===null) + $this->_parameters[$id]=$node->getValue(); + else + $this->_parameters[$id]=array($type,$properties->toArray()); + } + } + + // authorization + if(($authorizationNode=$dom->getElementByTagName('authorization'))!==null) + { + $rules=array(); + foreach($authorizationNode->getElements() as $node) + { + $pages=$node->getAttribute('pages'); + $ruleApplies=false; + if(empty($pages)) + $ruleApplies=true; + else if($page!==null) + { + $ps=explode(',',$pages); + foreach($ps as $p) + { + if($page===trim($p)) + { + $ruleApplies=true; + break; + } + } + } + if($ruleApplies) + $rules[]=new TAuthorizationRule($node->getTagName(),$node->getAttribute('users'),$node->getAttribute('roles'),$node->getAttribute('verb')); + } + $this->_rules=array_merge($rules,$this->_rules); + } + + // pages + if($page!==null && ($pagesNode=$dom->getElementByTagName('pages'))!==null) + { + $baseProperties=$pagesNode->getAttributes(); + foreach($pagesNode->getElementsByTagName('page') as $node) + { + $properties=$node->getAttributes(); + $type=$properties->remove('type'); + $id=$properties->itemAt('id'); + if($id===null || $type===null) + throw new TConfigurationException('pageservice_page_element_invalid',$fname); + if($id===$page) + { + $this->_properties=array_merge($baseProperties->toArray(),$properties->toArray()); + $this->_pageType=$type; + } + } + } + if($page!==null && $this->_pageType===null) + throw new THttpException('pageservice_page_inexistent',$page); + } +} + + + +?> \ No newline at end of file -- cgit v1.2.3