summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes17
-rw-r--r--HISTORY1
-rw-r--r--demos/quickstart/protected/pages/Advanced/Auth.page8
-rw-r--r--demos/quickstart/protected/pages/Configurations/PageConfig.page5
-rw-r--r--demos/quickstart/protected/pages/GettingStarted/NewFeatures.page3
-rw-r--r--framework/Exceptions/TErrorHandler.php2
-rw-r--r--framework/Security/TAuthorizationRule.php59
-rw-r--r--framework/Web/Services/TPageService.php127
-rw-r--r--tests/FunctionalTests/tickets/index700.php8
-rw-r--r--tests/FunctionalTests/tickets/protected700/application.xml32
-rw-r--r--tests/FunctionalTests/tickets/protected700/common/BasePage.php68
-rw-r--r--tests/FunctionalTests/tickets/protected700/layout/MainLayout.php12
-rw-r--r--tests/FunctionalTests/tickets/protected700/layout/MainLayout.tpl26
-rw-r--r--tests/FunctionalTests/tickets/protected700/pages/Home.page14
-rw-r--r--tests/FunctionalTests/tickets/protected700/pages/UserLogin.page28
-rw-r--r--tests/FunctionalTests/tickets/protected700/pages/UserLogin.php13
-rw-r--r--tests/FunctionalTests/tickets/protected700/pages/admin/Home.page14
-rw-r--r--tests/FunctionalTests/tickets/protected700/pages/admin/Home2.page14
-rw-r--r--tests/FunctionalTests/tickets/protected700/pages/admin/config.xml11
-rw-r--r--tests/FunctionalTests/tickets/protected700/pages/admin/users/Home.page14
-rw-r--r--tests/FunctionalTests/tickets/protected700/pages/admin/users/Home2.page14
-rw-r--r--tests/FunctionalTests/tickets/protected700/pages/admin/users/config.xml6
-rw-r--r--tests/FunctionalTests/tickets/protected700/pages/config.xml10
-rw-r--r--tests/FunctionalTests/tickets/protected700/pages/content/Home.page14
-rw-r--r--tests/FunctionalTests/tickets/tests/Ticket700TestCase.php72
25 files changed, 540 insertions, 52 deletions
diff --git a/.gitattributes b/.gitattributes
index 9f428965..55ca9840 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -2810,6 +2810,7 @@ tests/FunctionalTests/quickstart/Controls/Wizard5TestCase.php -text
tests/FunctionalTests/quickstart/Fundamentals/HangmanTestCase.php -text
tests/FunctionalTests/tickets.php -text
tests/FunctionalTests/tickets/index.php -text
+tests/FunctionalTests/tickets/index700.php -text
tests/FunctionalTests/tickets/protected/application.xml -text
tests/FunctionalTests/tickets/protected/controls/down.gif -text
tests/FunctionalTests/tickets/protected/controls/up.gif -text
@@ -2934,6 +2935,21 @@ tests/FunctionalTests/tickets/protected/pages/Ticket93.php -text
tests/FunctionalTests/tickets/protected/pages/ToggleTest.page -text
tests/FunctionalTests/tickets/protected/pages/config.xml -text
tests/FunctionalTests/tickets/protected/pages/hotspot.jpg -text
+tests/FunctionalTests/tickets/protected700/application.xml -text
+tests/FunctionalTests/tickets/protected700/common/BasePage.php -text
+tests/FunctionalTests/tickets/protected700/layout/MainLayout.php -text
+tests/FunctionalTests/tickets/protected700/layout/MainLayout.tpl -text
+tests/FunctionalTests/tickets/protected700/pages/Home.page -text
+tests/FunctionalTests/tickets/protected700/pages/UserLogin.page -text
+tests/FunctionalTests/tickets/protected700/pages/UserLogin.php -text
+tests/FunctionalTests/tickets/protected700/pages/admin/Home.page -text
+tests/FunctionalTests/tickets/protected700/pages/admin/Home2.page -text
+tests/FunctionalTests/tickets/protected700/pages/admin/config.xml -text
+tests/FunctionalTests/tickets/protected700/pages/admin/users/Home.page -text
+tests/FunctionalTests/tickets/protected700/pages/admin/users/Home2.page -text
+tests/FunctionalTests/tickets/protected700/pages/admin/users/config.xml -text
+tests/FunctionalTests/tickets/protected700/pages/config.xml -text
+tests/FunctionalTests/tickets/protected700/pages/content/Home.page -text
tests/FunctionalTests/tickets/tests/Ticket121TestCase.php -text
tests/FunctionalTests/tickets/tests/Ticket163TestCase.php -text
tests/FunctionalTests/tickets/tests/Ticket169TestCase.php -text
@@ -2969,6 +2985,7 @@ tests/FunctionalTests/tickets/tests/Ticket587TestCase.php -text
tests/FunctionalTests/tickets/tests/Ticket592TestCase.php -text
tests/FunctionalTests/tickets/tests/Ticket653TestCase.php -text
tests/FunctionalTests/tickets/tests/Ticket659TestCase.php -text
+tests/FunctionalTests/tickets/tests/Ticket700TestCase.php -text
tests/FunctionalTests/tickets/tests/Ticket72TestCase.php -text
tests/FunctionalTests/tickets/tests/Ticket93TestCase.php -text
tests/FunctionalTests/validators.php -text
diff --git a/HISTORY b/HISTORY
index c6f7b3b5..07ab61fa 100644
--- a/HISTORY
+++ b/HISTORY
@@ -18,6 +18,7 @@ ENH: Ticket#672 - ForceSecureConnection to THttpRequest (Carl)
ENH: Ticket#676 - Updated ActiveRecord Oracle Drives (Qiang)
ENH: Ticket#678 - Improved DateTimeFormatInfo performance (Stever)
ENH: Ticket#681 - TUrlMapping can construct custom URLs now (Qiang)
+ENH: Ticket#692,700 - Added support to wildcard in pages configuration; added IP filters in auth rules. (Qiang)
ENH: Added THead requirement check (Qiang)
ENH: Added TOutputCache.CacheTime (Qiang)
CHG: Ticket#685 - Slashes and backslashes mixup in PradoBase (Qiang)
diff --git a/demos/quickstart/protected/pages/Advanced/Auth.page b/demos/quickstart/protected/pages/Advanced/Auth.page
index a306c2ae..1c89e07d 100644
--- a/demos/quickstart/protected/pages/Advanced/Auth.page
+++ b/demos/quickstart/protected/pages/Advanced/Auth.page
@@ -74,6 +74,14 @@ When a page request is being processed, a list of authorization rules may be ava
<p id="720559" class="block-content">
In the above example, anonymous users will be denied from posting to <tt>PageID1</tt> and <tt>PageID2</tt>, while <tt>User1</tt> and <tt>User2</tt> and all users of role <tt>Role1</tt> can access the two pages (in both <tt>get</tt> and <tt>post</tt> methods).
</p>
+<com:SinceVersion Version="3.1.1" />
+<p class="block-content">
+Since version 3.1.1, the <tt>pages</tt> attribute in the authorization rules can take relative page paths with wildcard '*'. For example, <tt>pages="admin.Home"</tt> refers to the <tt>Home</tt> page under the <tt>admin</tt> directory, and <tt>pages="admin.*"</tt> would refer to all pages under the <tt>admin</tt> directory and subdirectories.
+</p>
+
+<p class="block-content">
+Also introduced in version 3.1.1 are IP rules. They are specified by a new attribute <tt>ip</tt> in authorization rules. The IP rules are used to determine if an authorization rule aplies to an end-user according to his IP address. One can list a few IPs together, separated by comma ','. Wildcard '*' can be used in the rules. For example, <tt>ip="192.168.0.2, 192.168.1.*"</tt> means the rule applies to users whose IP address is 192.168.0.2 or 192.168.1.*. The latter matches any host in the subnet 192.168.1.
+</p>
<h2 id="5504">Using <tt>TUserManager</tt></h2>
<p id="720560" class="block-content">
diff --git a/demos/quickstart/protected/pages/Configurations/PageConfig.page b/demos/quickstart/protected/pages/Configurations/PageConfig.page
index e41019af..c52164b6 100644
--- a/demos/quickstart/protected/pages/Configurations/PageConfig.page
+++ b/demos/quickstart/protected/pages/Configurations/PageConfig.page
@@ -39,4 +39,9 @@ The <tt>&lt;paths&gt;</tt>, <tt>&lt;modules&gt;</tt>, <tt>&lt;parameters&gt;</tt
Complete specification of page configurations can be found in the <a href="<%~../../../../../docs/specs/config.dtd%>">DTD</a> and <a href="<%~../../../../../docs/specs/config.xsd%>">XSD</a> files.
</p>
+<com:SinceVersion Version="3.1.1" />
+<p class="block-content">
+Since version 3.1.1, the <tt>id</tt> attribute in the &lt;page&gt; element can be a relative page path pointing to a page in the subdirectory of the directory containing the page configuration. For example, <tt>id="admin.Home"</tt> refers to the <tt>Home</tt> page under the <tt>admin</tt> directory. This enhancement allows developers to centralize their page configurations (e.g. put all page initializations in the aplication configuration or the root page configuration.)
+</p>
+
<div class="last-modified">$Id$</div></com:TContent> \ No newline at end of file
diff --git a/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page b/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page
index be754e65..e3e5c0e4 100644
--- a/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page
+++ b/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page
@@ -13,7 +13,8 @@ This page summarizes the main new features that are introduced in each PRADO rel
<li>Added a new control <a href="?page=Controls.Captcha">TCaptcha</a> that displays a CAPTCHA to keep spammers from signing up for certain accounts online. A related validator <tt>TCaptchaValidator</tt> is also implemented.</li>
<li>Added a new control <a href="?page=Controls.Slider">TSlider</a> that displays a slider which can be used for numeric input.</li>
<li>Added Oracle DB support to Active Record.</li>
-<li>Added support of TDataGrid to allow grouping consecutive cells with the same content.</li>
+<li>Added support to TDataGrid to allow grouping consecutive cells with the same content.</li>
+<li>Added support to allow configuring page properties and authorization rules using <a href="?page=Configurations.PageConfig">relative page paths</a> in application and page configurations. Added support to allow <a href="?page=Advanced.Auth">authorization</a> based on remote host address.</li>
</ul>
<h2 id="8006">Version 3.1.0</h2>
diff --git a/framework/Exceptions/TErrorHandler.php b/framework/Exceptions/TErrorHandler.php
index 2d245a2f..b98ade5e 100644
--- a/framework/Exceptions/TErrorHandler.php
+++ b/framework/Exceptions/TErrorHandler.php
@@ -300,7 +300,7 @@ class TErrorHandler extends TModule
// if PHP exception, we want to show the 2nd stack level context
// because the 1st stack level is of little use (it's in error handler)
if($exception instanceof TPhpErrorException)
- $result=$trace[1];
+ $result=isset($trace[0]['file'])?$trace[0]:$trace[1];
else if($exception instanceof TInvalidOperationException)
{
// in case of getter or setter error, find out the exact file and row
diff --git a/framework/Security/TAuthorizationRule.php b/framework/Security/TAuthorizationRule.php
index fa1eb134..5b3a3c25 100644
--- a/framework/Security/TAuthorizationRule.php
+++ b/framework/Security/TAuthorizationRule.php
@@ -14,7 +14,7 @@
*
* TAuthorizationRule represents a single authorization rule.
* A rule is specified by an action (required), a list of users (optional),
- * a list of roles (optional), and a verb (optional).
+ * a list of roles (optional), a verb (optional), and a list of IP rules (optional).
* Action can be either 'allow' or 'deny'.
* Guest (anonymous, unauthenticated) users are represented by question mark '?'.
* All users (including guest users) are represented by asterisk '*'.
@@ -22,6 +22,7 @@
* Users/roles are case-insensitive.
* Different users/roles are separated by comma ','.
* Verb can be either 'get' or 'post'. If it is absent, it means both.
+ * IP rules are separated by comma ',' and can contain wild card in the rules (e.g. '192.132.23.33, 192.122.*.*')
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
@@ -47,6 +48,10 @@ class TAuthorizationRule extends TComponent
*/
private $_verb;
/**
+ * @var string IP patterns
+ */
+ private $_ipRules;
+ /**
* @var boolean if this rule applies to everyone
*/
private $_everyone;
@@ -65,8 +70,9 @@ class TAuthorizationRule extends TComponent
* @param string a comma separated user list
* @param string a comma separated role list
* @param string verb, can be empty, 'get', or 'post'
+ * @param string IP rules (separated by comma, can contain wild card *)
*/
- public function __construct($action,$users,$roles,$verb='')
+ public function __construct($action,$users,$roles,$verb='',$ipRules='')
{
$action=strtolower(trim($action));
if($action==='allow' || $action==='deny')
@@ -75,6 +81,7 @@ class TAuthorizationRule extends TComponent
throw new TInvalidDataValueException('authorizationrule_action_invalid',$action);
$this->_users=array();
$this->_roles=array();
+ $this->_ipRules=array();
$this->_everyone=false;
$this->_guest=false;
$this->_authenticated=false;
@@ -105,6 +112,11 @@ class TAuthorizationRule extends TComponent
$this->_verb=$verb;
else
throw new TInvalidDataValueException('authorizationrule_verb_invalid',$verb);
+ foreach(explode(',',$ipRules) as $ipRule)
+ {
+ if(($ipRule=trim($ipRule))!=='')
+ $this->_ipRules[]=$ipRule;
+ }
}
/**
@@ -140,6 +152,15 @@ class TAuthorizationRule extends TComponent
}
/**
+ * @return array list of IP rules.
+ * @since 3.1.1
+ */
+ public function getIPRules()
+ {
+ return $this->_ipRules;
+ }
+
+ /**
* @return boolean if this rule applies to everyone
*/
public function getGuestApplied()
@@ -171,6 +192,8 @@ class TAuthorizationRule extends TComponent
$decision=($this->_action==='allow')?1:-1;
if($this->_verb==='' || strcasecmp($verb,$this->_verb)===0)
{
+ if(!$this->isHostAddressMatched())
+ return 0;
if($this->_everyone || ($this->_guest && $user->getIsGuest()) || ($this->_authenticated && !$user->getIsGuest()))
return $decision;
if(in_array(strtolower($user->getName()),$this->_users))
@@ -181,6 +204,38 @@ class TAuthorizationRule extends TComponent
}
return 0;
}
+
+ private function isHostAddressMatched()
+ {
+ if(empty($this->_ipRules))
+ return 1;
+ $ip=Prado::getApplication()->getRequest()->getUserHostAddress();
+ foreach($this->_ipRules as $rule)
+ {
+ if($rule===$ip)
+ return 1;
+ if(strpos($rule,'*')!==false)
+ {
+ $a=explode('.',$rule);
+ $b=explode('.',$ip);
+ if(isset($a[3]) && isset($b[3]))
+ {
+ $matched=true;
+ for($i=0;$i<4;++$i)
+ {
+ if($a[$i]!==$b[i] && $a[$i]!=='*')
+ {
+ $matched=false;
+ break;
+ }
+ }
+ if($matched)
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
}
diff --git a/framework/Web/Services/TPageService.php b/framework/Web/Services/TPageService.php
index ab8d3651..8b7fb52d 100644
--- a/framework/Web/Services/TPageService.php
+++ b/framework/Web/Services/TPageService.php
@@ -137,7 +137,7 @@ class TPageService extends TService
{
Prado::trace("Initializing TPageService",'System.Web.Services.TPageService');
- $pageConfig=$this->loadPageConfig($this->getRequestedPagePath(),$config);
+ $pageConfig=$this->loadPageConfig($config);
$this->initPageContext($pageConfig);
@@ -169,17 +169,19 @@ class TPageService extends TService
// initial page properties (to be set when page runs)
$this->_properties=$config->getProperties();
$this->getApplication()->getAuthorizationRules()->mergeWith($config->getRules());
+ $pagePath=$this->getRequestedPagePath();
// external configurations
- foreach($config->getExternalConfigurations() as $filePath=>$condition)
+ foreach($config->getExternalConfigurations() as $filePath=>$params)
{
+ list($configPagePath,$condition)=$params;
if($condition!==true)
$condition=$this->evaluateExpression($condition);
if($condition)
{
if(($path=Prado::getPathOfNamespace($filePath,TApplication::CONFIG_FILE_EXT))===null || !is_file($path))
throw new TConfigurationException('pageservice_includefile_invalid',$filePath);
- $c=new TPageConfiguration;
- $c->loadFromFile($path,null);
+ $c=new TPageConfiguration($pagePath);
+ $c->loadFromFile($path,$configPagePath);
$this->applyConfiguration($c);
}
}
@@ -200,19 +202,19 @@ class TPageService extends TService
/**
* Collects configuration for a page.
- * @param string page path in the format of Path.To.Page
- * @param TXmlElement additional configuration
+ * @param TXmlElement additional configuration specified in the application configuration
* @return TPageConfiguration
*/
- protected function loadPageConfig($pagePath,$config=null)
+ protected function loadPageConfig($config)
{
$application=$this->getApplication();
+ $pagePath=$this->getRequestedPagePath();
if(($cache=$application->getCache())===null)
{
- $pageConfig=new TPageConfiguration;
+ $pageConfig=new TPageConfiguration($pagePath);
if($config!==null)
- $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath());
- $pageConfig->loadFromFiles($pagePath,$this->getBasePath());
+ $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath(),'');
+ $pageConfig->loadFromFiles($this->getBasePath());
}
else
{
@@ -258,10 +260,10 @@ class TPageService extends TService
}
if(!$configCached)
{
- $pageConfig=new TPageConfiguration;
+ $pageConfig=new TPageConfiguration($pagePath);
if($config!==null)
- $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath());
- $pageConfig->loadFromFiles($pagePath,$this->getBasePath());
+ $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath(),'');
+ $pageConfig->loadFromFiles($this->getBasePath());
$cache->set(self::CONFIG_CACHE_PREFIX.$this->getID().$pagePath,array($pageConfig,$currentTimestamp));
}
}
@@ -517,6 +519,19 @@ class TPageConfiguration extends TComponent
* @var array list of included configurations
*/
private $_includes=array();
+ /**
+ * @var string the currently request page in the format of Path.To.PageName
+ */
+ private $_pagePath='';
+
+ /**
+ * Constructor.
+ * @param string the currently request page in the format of Path.To.PageName
+ */
+ public function __construct($pagePath)
+ {
+ $this->_pagePath=$pagePath;
+ }
/**
* @return array list of external configuration files. Each element is like $filePath=>$condition
@@ -558,36 +573,40 @@ class TPageConfiguration extends TComponent
/**
* 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 loadFromFiles($pagePath,$basePath)
+ public function loadFromFiles($basePath)
{
- $paths=explode('.',$pagePath);
+ $paths=explode('.',$this->_pagePath);
$page=array_pop($paths);
$path=$basePath;
+ $configPagePath='';
foreach($paths as $p)
{
- $this->loadFromFile($path.DIRECTORY_SEPARATOR.TPageService::CONFIG_FILE,null);
+ $this->loadFromFile($path.DIRECTORY_SEPARATOR.TPageService::CONFIG_FILE,$configPagePath);
$path.=DIRECTORY_SEPARATOR.$p;
+ if($configPagePath==='')
+ $configPagePath=$p;
+ else
+ $configPagePath.='.'.$p;
}
- $this->loadFromFile($path.DIRECTORY_SEPARATOR.TPageService::CONFIG_FILE,$page);
+ $this->loadFromFile($path.DIRECTORY_SEPARATOR.TPageService::CONFIG_FILE,$configPagePath);
$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
+ * @param string the page path that the config file is associated with. The page path doesn't include the page name.
*/
- public function loadFromFile($fname,$page)
+ public function loadFromFile($fname,$configPagePath)
{
- Prado::trace("Loading $page with file $fname",'System.Web.Services.TPageService');
+ Prado::trace("Loading page configuration file $fname",'System.Web.Services.TPageService');
if(empty($fname) || !is_file($fname))
return;
$dom=new TXmlDocument;
if($dom->loadFromFile($fname))
- $this->loadFromXml($dom,dirname($fname),$page);
+ $this->loadFromXml($dom,dirname($fname),$configPagePath);
else
throw new TConfigurationException('pageserviceconf_file_invalid',$fname);
}
@@ -597,13 +616,13 @@ class TPageConfiguration extends TComponent
* The configuration includes information for both application
* and page service.
* @param TXmlElement config xml element
- * @param string base path corresponding to this xml element
- * @param string page name, null if page is not required
+ * @param string the directory containing this configuration
+ * @param string the page path that the config XML is associated with. The page path doesn't include the page name.
*/
- public function loadFromXml($dom,$configPath,$page=null)
+ public function loadFromXml($dom,$configPath,$configPagePath)
{
$this->loadApplicationConfigurationFromXml($dom,$configPath);
- $this->loadPageConfigurationFromXml($dom,$configPath,$page);
+ $this->loadPageConfigurationFromXml($dom,$configPath,$configPagePath);
}
/**
@@ -622,9 +641,9 @@ class TPageConfiguration extends TComponent
* Loads the configuration specific for page service.
* @param TXmlElement config xml element
* @param string base path corresponding to this xml element
- * @param string page name, null if page is not required
+ * @param string the page path that the config XML is associated with. The page path doesn't include the page name.
*/
- public function loadPageConfigurationFromXml($dom,$configPath,$page=null)
+ public function loadPageConfigurationFromXml($dom,$configPath,$configPagePath)
{
// authorization
if(($authorizationNode=$dom->getElementByTagName('authorization'))!==null)
@@ -632,24 +651,38 @@ class TPageConfiguration extends TComponent
$rules=array();
foreach($authorizationNode->getElements() as $node)
{
- $pages=$node->getAttribute('pages');
+ $patterns=$node->getAttribute('pages');
$ruleApplies=false;
- if(empty($pages))
+ if(empty($patterns)) // null or empty string
$ruleApplies=true;
- else if($page!==null)
+ else
{
- $ps=explode(',',$pages);
- foreach($ps as $p)
+ foreach(explode(',',$patterns) as $pattern)
{
- if(strcasecmp($page,trim($p))===0)
+ if(($pattern=trim($pattern))!=='')
{
- $ruleApplies=true;
- break;
+ // we know $configPagePath and $this->_pagePath
+ if($configPagePath!=='') // prepend the pattern with ConfigPagePath
+ $pattern=$configPagePath.'.'.$pattern;
+ if(strcasecmp($pattern,$this->_pagePath)===0)
+ {
+ $ruleApplies=true;
+ break;
+ }
+ if($pattern[strlen($pattern)-1]==='*') // try wildcard matching
+ {
+ $pattern=strtolower(substr($pattern,0,strlen($pattern)-1));
+ if(strpos(strtolower($this->_pagePath),$pattern)===0)
+ {
+ $ruleApplies=true;
+ break;
+ }
+ }
}
}
}
if($ruleApplies)
- $rules[]=new TAuthorizationRule($node->getTagName(),$node->getAttribute('users'),$node->getAttribute('roles'),$node->getAttribute('verb'));
+ $rules[]=new TAuthorizationRule($node->getTagName(),$node->getAttribute('users'),$node->getAttribute('roles'),$node->getAttribute('verb'),$node->getAttribute('ip'));
}
$this->_rules=array_merge($rules,$this->_rules);
}
@@ -658,16 +691,14 @@ class TPageConfiguration extends TComponent
if(($pagesNode=$dom->getElementByTagName('pages'))!==null)
{
$this->_properties=array_merge($this->_properties,$pagesNode->getAttributes()->toArray());
- if($page!==null) // at the page folder
+ // at the page folder
+ foreach($pagesNode->getElementsByTagName('page') as $node)
{
- foreach($pagesNode->getElementsByTagName('page') as $node)
- {
- $properties=$node->getAttributes();
- if(($id=$properties->itemAt('id'))===null)
- throw new TConfigurationException('pageserviceconf_page_invalid',$configPath);
- if(strcasecmp($id,$page)===0)
- $this->_properties=array_merge($this->_properties,$properties->toArray());
- }
+ $properties=$node->getAttributes();
+ if(($id=$properties->remove('id'))===null)
+ throw new TConfigurationException('pageserviceconf_page_invalid',$configPath);
+ if(($configPagePath==='' && strcasecmp($id,$this->_pagePath)===0) || ($configPath!=='' && strcasecmp($configPagePath.'.'.$id,$this->_pagePath)===0))
+ $this->_properties=array_merge($this->_properties,$properties->toArray());
}
}
@@ -679,9 +710,9 @@ class TPageConfiguration extends TComponent
if(($filePath=$node->getAttribute('file'))===null)
throw new TConfigurationException('pageserviceconf_includefile_required');
if(isset($this->_includes[$filePath]))
- $this->_includes[$filePath]='('.$this->_includes[$filePath].') || ('.$when.')';
+ $this->_includes[$filePath]=array($configPagePath,'('.$this->_includes[$filePath][1].') || ('.$when.')');
else
- $this->_includes[$filePath]=$when;
+ $this->_includes[$filePath]=array($configPagePath,$when);
}
}
}
diff --git a/tests/FunctionalTests/tickets/index700.php b/tests/FunctionalTests/tickets/index700.php
new file mode 100644
index 00000000..d35f789f
--- /dev/null
+++ b/tests/FunctionalTests/tickets/index700.php
@@ -0,0 +1,8 @@
+<?php
+
+require_once(dirname(__FILE__).'/../../../framework/prado.php');
+
+$app=new TApplication('protected700/application.xml');
+$app->run();
+
+?> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected700/application.xml b/tests/FunctionalTests/tickets/protected700/application.xml
new file mode 100644
index 00000000..cb00aae3
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected700/application.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<application id="Ticket700Tests" Mode="Debug">
+<paths>
+ <using namespace="Application.common.*" />
+</paths>
+
+<services>
+ <service id="page" class="TPageService" BasePageClass="Application.common.BasePage">
+ <modules>
+ <module id="users" class="System.Security.TUserManager" PasswordMode="Clear">
+ <user name="AdminUser" password="demo" />
+ <user name="NormalUser" password="demo" />
+ <role name="admin" users="AdminUser" />
+ <role name="user" users="NormalUser" />
+ </module>
+ <module id="auth" class="System.Security.TAuthManager" UserManager="users" LoginPage="UserLogin" />
+ </modules>
+
+ <authorization>
+ <allow pages="Home" users="*" />
+ <deny pages="admin.*" users="?" />
+ <deny pages="content.*" users="*" />
+ <allow users="*" />
+ </authorization>
+
+ <pages MasterClass="Application.layout.MainLayout" Param1="Set at app config" Param5="Set at app config">
+ <page id="Home" Param2="Set at app config" />
+ <page id="admin.Home" Param3="Set at app config" Param4="Set at app config" />
+ </pages>
+ </service>
+</services>
+</application>
diff --git a/tests/FunctionalTests/tickets/protected700/common/BasePage.php b/tests/FunctionalTests/tickets/protected700/common/BasePage.php
new file mode 100644
index 00000000..1e40f754
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected700/common/BasePage.php
@@ -0,0 +1,68 @@
+<?php
+
+class BasePage extends TPage
+{
+ private $_param1='default 1';
+ private $_param2='default 2';
+ private $_param3='default 3';
+ private $_param4='default 4';
+ private $_param5='default 5';
+
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ $this->Title=$this->PagePath;
+ }
+
+ public function getParam1()
+ {
+ return $this->_param1;
+ }
+
+ public function setParam1($value)
+ {
+ $this->_param1=$value;
+ }
+
+ public function getParam2()
+ {
+ return $this->_param2;
+ }
+
+ public function setParam2($value)
+ {
+ $this->_param2=$value;
+ }
+
+ public function getParam3()
+ {
+ return $this->_param3;
+ }
+
+ public function setParam3($value)
+ {
+ $this->_param3=$value;
+ }
+
+ public function getParam4()
+ {
+ return $this->_param4;
+ }
+
+ public function setParam4($value)
+ {
+ $this->_param4=$value;
+ }
+
+ public function getParam5()
+ {
+ return $this->_param5;
+ }
+
+ public function setParam5($value)
+ {
+ $this->_param5=$value;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected700/layout/MainLayout.php b/tests/FunctionalTests/tickets/protected700/layout/MainLayout.php
new file mode 100644
index 00000000..3e0a3d19
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected700/layout/MainLayout.php
@@ -0,0 +1,12 @@
+<?php
+
+class MainLayout extends TTemplateControl
+{
+ public function logout($sender,$param)
+ {
+ $this->Application->getModule('auth')->logout();
+ $this->Response->reload();
+ }
+}
+
+?> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected700/layout/MainLayout.tpl b/tests/FunctionalTests/tickets/protected700/layout/MainLayout.tpl
new file mode 100644
index 00000000..acbfa0e6
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected700/layout/MainLayout.tpl
@@ -0,0 +1,26 @@
+<html>
+<com:THead />
+<body>
+
+<com:TForm>
+
+<h1><%= $this->Page->Title %></h1>
+
+<com:TContentPlaceHolder ID="Main" />
+
+<hr/>
+<ul>
+<li><a id="pageHome" href="<%=$this->Service->constructUrl('Home')%>">Home</a></li>
+<li><a id="pageAdminHome" href="<%=$this->Service->constructUrl('admin.Home')%>">admin.Home</a></li>
+<li><a id="pageAdminHome2" href="<%=$this->Service->constructUrl('admin.Home2')%>">admin.Home2</a></li>
+<li><a id="pageAdminUsersHome" href="<%=$this->Service->constructUrl('admin.users.Home')%>">admin.users.Home</a></li>
+<li><a id="pageAdminUsersHome2" href="<%=$this->Service->constructUrl('admin.users.Home2')%>">admin.users.Home2</a></li>
+<li><a id="pageContentHome" href="<%=$this->Service->constructUrl('content.Home')%>">content.Home</a></li>
+</ul>
+<hr/>
+<a href="<%=$this->Service->constructUrl('UserLogin')%>">Login</a> |
+<com:TLinkButton ID="Logout" Text="Logout" OnClick="logout" /> (<%= $this->User->Name %>)
+</com:TForm>
+
+</body>
+</html> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected700/pages/Home.page b/tests/FunctionalTests/tickets/protected700/pages/Home.page
new file mode 100644
index 00000000..7d1c1187
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected700/pages/Home.page
@@ -0,0 +1,14 @@
+<com:TContent ID="Main">
+
+|Param1: <%= $this->Param1 %>|
+<br/>
+|Param2: <%= $this->Param2 %>|
+<br/>
+|Param3: <%= $this->Param3 %>|
+<br/>
+|Param4: <%= $this->Param4 %>|
+<br/>
+|Param5: <%= $this->Param5 %>|
+<br/>
+
+</com:TContent> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected700/pages/UserLogin.page b/tests/FunctionalTests/tickets/protected700/pages/UserLogin.page
new file mode 100644
index 00000000..07d4ece9
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected700/pages/UserLogin.page
@@ -0,0 +1,28 @@
+<com:TContent ID="Main" >
+
+ <com:TLabel
+ ForControl="Username"
+ Text="User Name"
+ CssClass="label"/>
+ <com:TTextBox ID="Username"
+ AccessKey="u"
+ ValidationGroup="login"
+ CssClass="textbox"/>
+<br/>
+ <com:TLabel
+ ForControl="Password"
+ Text="Password"
+ CssClass="label"/>
+ <com:TTextBox ID="Password"
+ AccessKey="p"
+ CssClass="textbox"
+ ValidationGroup="login"
+ TextMode="Password"/>
+<br/>
+ <com:TButton ID="LoginButton"
+ OnClick="loginButtonClicked"
+ Text="Login"
+ ValidationGroup="login"
+ CssClass="button"/>
+
+</com:TContent> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected700/pages/UserLogin.php b/tests/FunctionalTests/tickets/protected700/pages/UserLogin.php
new file mode 100644
index 00000000..37258879
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected700/pages/UserLogin.php
@@ -0,0 +1,13 @@
+<?php
+
+class UserLogin extends BasePage
+{
+ public function loginButtonClicked($sender,$param)
+ {
+ $authManager=$this->Application->getModule('auth');
+ $authManager->login($this->Username->Text,$this->Password->Text);
+ $this->Response->redirect($this->Service->constructUrl('Home'));
+ }
+}
+
+?> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected700/pages/admin/Home.page b/tests/FunctionalTests/tickets/protected700/pages/admin/Home.page
new file mode 100644
index 00000000..7d1c1187
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected700/pages/admin/Home.page
@@ -0,0 +1,14 @@
+<com:TContent ID="Main">
+
+|Param1: <%= $this->Param1 %>|
+<br/>
+|Param2: <%= $this->Param2 %>|
+<br/>
+|Param3: <%= $this->Param3 %>|
+<br/>
+|Param4: <%= $this->Param4 %>|
+<br/>
+|Param5: <%= $this->Param5 %>|
+<br/>
+
+</com:TContent> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected700/pages/admin/Home2.page b/tests/FunctionalTests/tickets/protected700/pages/admin/Home2.page
new file mode 100644
index 00000000..7d1c1187
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected700/pages/admin/Home2.page
@@ -0,0 +1,14 @@
+<com:TContent ID="Main">
+
+|Param1: <%= $this->Param1 %>|
+<br/>
+|Param2: <%= $this->Param2 %>|
+<br/>
+|Param3: <%= $this->Param3 %>|
+<br/>
+|Param4: <%= $this->Param4 %>|
+<br/>
+|Param5: <%= $this->Param5 %>|
+<br/>
+
+</com:TContent> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected700/pages/admin/config.xml b/tests/FunctionalTests/tickets/protected700/pages/admin/config.xml
new file mode 100644
index 00000000..04ac6bdd
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected700/pages/admin/config.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+ <authorization>
+ <allow pages="Home2" users="*" />
+ <deny pages="users.Home" roles="user" />
+ </authorization>
+ <pages Param2="Set at admin">
+ <page id="users.Home" Param1="Set at admin" Param4="Set at admin" />
+ <page id="Home" Param3="Set at admin" />
+ </pages>
+</configuration> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected700/pages/admin/users/Home.page b/tests/FunctionalTests/tickets/protected700/pages/admin/users/Home.page
new file mode 100644
index 00000000..7d1c1187
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected700/pages/admin/users/Home.page
@@ -0,0 +1,14 @@
+<com:TContent ID="Main">
+
+|Param1: <%= $this->Param1 %>|
+<br/>
+|Param2: <%= $this->Param2 %>|
+<br/>
+|Param3: <%= $this->Param3 %>|
+<br/>
+|Param4: <%= $this->Param4 %>|
+<br/>
+|Param5: <%= $this->Param5 %>|
+<br/>
+
+</com:TContent> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected700/pages/admin/users/Home2.page b/tests/FunctionalTests/tickets/protected700/pages/admin/users/Home2.page
new file mode 100644
index 00000000..7d1c1187
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected700/pages/admin/users/Home2.page
@@ -0,0 +1,14 @@
+<com:TContent ID="Main">
+
+|Param1: <%= $this->Param1 %>|
+<br/>
+|Param2: <%= $this->Param2 %>|
+<br/>
+|Param3: <%= $this->Param3 %>|
+<br/>
+|Param4: <%= $this->Param4 %>|
+<br/>
+|Param5: <%= $this->Param5 %>|
+<br/>
+
+</com:TContent> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected700/pages/admin/users/config.xml b/tests/FunctionalTests/tickets/protected700/pages/admin/users/config.xml
new file mode 100644
index 00000000..175ea5c7
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected700/pages/admin/users/config.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+ <pages>
+ <page id="Home" Param2="Set at admin.users" />
+ </pages>
+</configuration> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected700/pages/config.xml b/tests/FunctionalTests/tickets/protected700/pages/config.xml
new file mode 100644
index 00000000..416de43f
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected700/pages/config.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+ <authorization>
+ <allow pages="content.*" users="*" />
+ </authorization>
+ <pages Param2="Set at root">
+ <page id="users.Home" Param1="Set at admin" Param4="Set at admin" />
+ <page id="Home" Param5="Set at root" />
+ </pages>
+</configuration> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected700/pages/content/Home.page b/tests/FunctionalTests/tickets/protected700/pages/content/Home.page
new file mode 100644
index 00000000..7d1c1187
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected700/pages/content/Home.page
@@ -0,0 +1,14 @@
+<com:TContent ID="Main">
+
+|Param1: <%= $this->Param1 %>|
+<br/>
+|Param2: <%= $this->Param2 %>|
+<br/>
+|Param3: <%= $this->Param3 %>|
+<br/>
+|Param4: <%= $this->Param4 %>|
+<br/>
+|Param5: <%= $this->Param5 %>|
+<br/>
+
+</com:TContent> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/tests/Ticket700TestCase.php b/tests/FunctionalTests/tickets/tests/Ticket700TestCase.php
new file mode 100644
index 00000000..e7ff30b3
--- /dev/null
+++ b/tests/FunctionalTests/tickets/tests/Ticket700TestCase.php
@@ -0,0 +1,72 @@
+<?php
+class Ticket700TestCase extends SeleniumTestCase
+{
+ function test()
+ {
+ // page: Home
+ $this->open('tickets/index700.php');
+ $this->clickAndWait('ctl0_Logout');
+ $this->clickAndWait('pageHome');
+ $this->assertTitle("Home");
+ $this->assertTextPresent('|Param1: Set at app config|');
+ $this->assertTextPresent('|Param2: Set at root|');
+ $this->assertTextPresent('|Param3: default 3|');
+ $this->assertTextPresent('|Param4: default 4|');
+ $this->assertTextPresent('|Param5: Set at root|');
+
+ // page: admin.Home
+ $this->clickAndWait('pageAdminHome');
+ $this->assertTitle('UserLogin');
+ $this->type('ctl0_Main_Username','AdminUser');
+ $this->type('ctl0_Main_Password','demo');
+ $this->clickAndWait('ctl0_Main_LoginButton');
+ $this->clickAndWait('pageAdminHome');
+ $this->assertTitle('admin.Home');
+ $this->assertTextPresent('|Param1: Set at app config|');
+ $this->assertTextPresent('|Param2: Set at admin|');
+ $this->assertTextPresent('|Param3: Set at admin|');
+ $this->assertTextPresent('|Param4: Set at app config|');
+ $this->assertTextPresent('|Param5: Set at app config|');
+
+ // page: admin.Home2
+ $this->clickAndWait('pageAdminHome2');
+ $this->assertTitle('admin.Home2');
+ $this->clickAndWait('ctl0_Logout');
+ $this->clickAndWait('pageAdminHome2');
+ $this->assertTitle('admin.Home2');
+
+ // page: admin.users.Home
+ $this->clickAndWait('pageAdminUsersHome');
+ $this->assertTitle('UserLogin');
+ $this->type('ctl0_Main_Username','NormalUser');
+ $this->type('ctl0_Main_Password','demo');
+ $this->clickAndWait('ctl0_Main_LoginButton');
+ $this->clickAndWait('pageAdminUsersHome');
+ $this->assertTitle('UserLogin');
+ $this->type('ctl0_Main_Username','AdminUser');
+ $this->type('ctl0_Main_Password','demo');
+ $this->clickAndWait('ctl0_Main_LoginButton');
+ $this->clickAndWait('pageAdminUsersHome');
+ $this->assertTitle('admin.users.Home');
+ $this->assertTextPresent('|Param1: Set at admin|');
+ $this->assertTextPresent('|Param2: Set at admin.users|');
+ $this->assertTextPresent('|Param3: default 3|');
+ $this->assertTextPresent('|Param4: Set at admin|');
+ $this->assertTextPresent('|Param5: Set at app config|');
+
+ // page: admin.users.Home2
+ $this->clickAndWait('pageAdminUsersHome2');
+ $this->assertTitle('admin.users.Home2');
+
+ // page: content.Home
+ $this->clickAndWait('pageContentHome');
+ $this->assertTitle('content.Home');
+ $this->assertTextPresent('|Param1: Set at app config|');
+ $this->assertTextPresent('|Param2: Set at root|');
+ $this->assertTextPresent('|Param3: default 3|');
+ $this->assertTextPresent('|Param4: default 4|');
+ $this->assertTextPresent('|Param5: Set at app config|');
+ $this->clickAndWait('ctl0_Logout');
+ }
+}
+?> \ No newline at end of file