summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgodzilla80@gmx.net <>2010-02-14 01:22:57 +0000
committergodzilla80@gmx.net <>2010-02-14 01:22:57 +0000
commit94e94e0a8566f23d16658a04c55b0bbfdd6689aa (patch)
tree72ffad82c279080dd9320d45dda26d64ffb4626f
parent966fd66f217911d079c4bd6a87b09f4a0c5c4736 (diff)
Merge Branches & Trunk
/trunk:r2680,2692,2707-2736 /branches/3.1:r2682-2686,2694-2702,2705,2738-2762
-rw-r--r--.gitattributes4
-rw-r--r--HISTORY36
-rw-r--r--UPGRADE7
-rw-r--r--buildscripts/texbuilder/quickstart/quickstart.tex2
-rw-r--r--demos/quickstart/protected/pages/Controls/JavascriptLogger.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/id/JavascriptLogger.page2
-rw-r--r--framework/3rdParty/WsdlGen/Wsdl.php19
-rw-r--r--framework/3rdParty/readme.html2
-rw-r--r--framework/Caching/TDbCache.php11
-rw-r--r--framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php4
-rw-r--r--framework/Data/Common/Mysql/TMysqlMetaData.php14
-rw-r--r--framework/Data/Common/TDbCommandBuilder.php154
-rw-r--r--framework/Data/Common/TDbTableInfo.php24
-rw-r--r--framework/Data/DataGateway/TDataGatewayCommand.php5
-rw-r--r--framework/Data/DataGateway/TSqlCriteria.php72
-rw-r--r--framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php2
-rw-r--r--framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php2
-rw-r--r--framework/Data/SqlMap/DataMapper/TSqlMapCache.php80
-rw-r--r--framework/Data/SqlMap/TSqlMapConfig.php43
-rw-r--r--framework/Data/TDbConnection.php53
-rw-r--r--framework/Exceptions/messages/messages-fr.txt1
-rw-r--r--framework/Exceptions/messages/messages-id.txt1
-rw-r--r--framework/Exceptions/messages/messages-zh.txt1
-rw-r--r--framework/Exceptions/messages/messages.txt1
-rw-r--r--framework/TShellApplication.php10
-rw-r--r--framework/Util/TLogRouter.php87
-rwxr-xr-xframework/Web/Javascripts/source/prado/activecontrols/activedatepicker.js15
-rwxr-xr-xframework/Web/Javascripts/source/prado/activefileupload/activefileupload.js41
-rw-r--r--framework/Web/Javascripts/source/prado/validator/validation3.js5
-rw-r--r--framework/Web/THttpRequest.php6
-rw-r--r--framework/Web/THttpResponse.php29
-rw-r--r--framework/Web/THttpSession.php12
-rwxr-xr-xframework/Web/UI/ActiveControls/TDropContainer.php7
-rw-r--r--framework/Web/UI/WebControls/TBaseValidator.php3
-rw-r--r--framework/Web/UI/WebControls/TBoundColumn.php2
-rw-r--r--framework/Web/UI/WebControls/TCheckBoxList.php99
-rw-r--r--framework/Web/UI/WebControls/TDataGridColumn.php6
-rw-r--r--framework/Web/UI/WebControls/TJavascriptLogger.php6
-rw-r--r--framework/Web/UI/WebControls/TRegularExpressionValidator.php4
-rw-r--r--framework/Web/UI/WebControls/TTabPanel.php1
-rw-r--r--framework/Web/UI/WebControls/TTextBox.php14
-rw-r--r--framework/Web/UI/WebControls/TValidationSummary.php17
-rw-r--r--framework/pradolite.php9489
-rw-r--r--index.html1
-rwxr-xr-xtests/FunctionalTests/active-controls/protected/pages/ActiveDatePicker.page11
-rwxr-xr-xtests/FunctionalTests/active-controls/protected/pages/ActiveDatePicker.php6
-rwxr-xr-xtests/FunctionalTests/active-controls/tests/ActiveDatePickerTestCase.php10
-rw-r--r--tests/FunctionalTests/tickets/protected/pages/Issue216.page15
-rw-r--r--tests/FunctionalTests/tickets/protected/pages/Issue216.php14
-rw-r--r--tests/FunctionalTests/tickets/tests/Issue216TestCase.php28
50 files changed, 10294 insertions, 186 deletions
diff --git a/.gitattributes b/.gitattributes
index 868a31ba..964ffa6d 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -2896,6 +2896,7 @@ framework/powered2.gif -text
framework/prado-cli -text
framework/prado-cli.bat -text
framework/prado.php -text
+framework/pradolite.php -text
/index.html -text
/phing -text
/phing.bat -text
@@ -3127,6 +3128,8 @@ tests/FunctionalTests/tickets/protected/messages/en/messages.xml -text
tests/FunctionalTests/tickets/protected/pages/DActiveDropDownList2.php -text
tests/FunctionalTests/tickets/protected/pages/Issue120.page -text
tests/FunctionalTests/tickets/protected/pages/Issue120.php -text
+tests/FunctionalTests/tickets/protected/pages/Issue216.page -text
+tests/FunctionalTests/tickets/protected/pages/Issue216.php -text
tests/FunctionalTests/tickets/protected/pages/Layout.php -text
tests/FunctionalTests/tickets/protected/pages/Layout.tpl -text
tests/FunctionalTests/tickets/protected/pages/TestHtmlArea.php -text
@@ -3301,6 +3304,7 @@ 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/Issue120TestCase.php -text
+tests/FunctionalTests/tickets/tests/Issue216TestCase.php -text
tests/FunctionalTests/tickets/tests/Ticket121TestCase.php -text
tests/FunctionalTests/tickets/tests/Ticket163TestCase.php -text
tests/FunctionalTests/tickets/tests/Ticket169TestCase.php -text
diff --git a/HISTORY b/HISTORY
index 68d4d983..47a884e0 100644
--- a/HISTORY
+++ b/HISTORY
@@ -13,13 +13,46 @@ ENH: Issue#173 - Add "dragdropextra" (superghosting) patch, mouse coordinates an
NEW: Add TActiveMultiView (LCS Team)
NEW: Beta of master/slave senario solution (Yves)
-Version 3.1.6 to be released
+Version 3.1.7 To be released
+ENH: Issue#24 - Specify needed fields on demand (Yves)
+BUG: Issue#80 - Inconsistencies in TRegularExpressionValidator (Christophe)
+BUG: Issue#86 - THttpSession.CookieMode ignored / Session ID leak (Christophe)
+BUG: Issue#94 - DataGrid header/footer renderers unable to locate their parent grid in setData() method (Christophe)
+BUG: Issue#151 - TTextBox fails to display inital line break (Yves)
+BUG: Issue#153 - Bug with calls like MyActiveRedorc->withText()->withUser()->find(...) and null result (Christophe)
+BUG: Issue#157 - Enabled does not work properly on TActiveRadioButton/CheckBoxList controls (Bradley, Carl)
+BUG: Issue#166 - E_NOTICE level error in TDataGatewayCommand (Carl)
+BUG: Issue#169 - FlushOnExecute on Basic CacheModel flushes all Application Cache (E.Letard, Christophe)
+BUG: Issue#171 - <connection> tag in SqlMap config ignored in 3.1.5 and above, introduced by solving Issue#68 (Yves)
+EHN: Issue#184 - THttpResponse doesn't support custom Content-Type headers, remove charset part of header if THttpResponse.Charset=false (Yves)
+BUG: Issue#188 - TDbCache doesn't check if db connection is active. (Yves)
+BUG: Issue#189 - Page State corrupted when EnableStateValidation=False (Christophe)
+BUG: Issue#191 - Bad parsing of MySQL ENUM type column (Yves)
+BUG: Issue#192 - soap-enc:Array not a valid complex type (mosonyi at esix.hu)
+BUG: Issue#198 - "Undefined variable: tagName" after error in application configuration. (Christophe)
+BUG: Issue#200 - TShellApplication failed when no service are defined in application configuration. (Christophe)
+BUG: Issue#208 - TDbConnection.Charset not working properly (googlenew at pcforum.hu, Christophe)
+BUG: Issue#212 - Mistaken query executed by TMysqlMetaData (pbenny, Christophe)
+BUG: Issue#216 - TTabPanel doesn't preserve active tab on callback request (googlenew at pcforum.hu,Christophe)
+BUG: Typo in TBoundColumn (Robin)
+BUG: TActiveDatePicker js error when date format does not have the 3 elements (Christophe)
+ENH: Add property ClientScriptManagerClass to TPageService and releated changes in TPage.getClientScript() (Yves)
+ENH: Always render clientside counterparts of validation control even if not enabled, but pass-through Enabled property, to allow Enabled/Disable of validator on callback. (Yves)
+EHN: Add property TValidationSummary.ScrollToSummary to server-side control since property exists on client-side. (Yves)
+EHN: Add property TransactionClass (defaults to System.Data.TDbTransaction) to TDbConnection and modify beginTransaction() (Yves)
+ENH: Modify TDbTableInfo::getColumnNames() to store result in private class member (Yves)
+ENH: Issue#215 - Add ClientSide property to TDropContainer (googlenew at pcforum.hu, Christophe)
+CHG: Issue#218 - Change URL of Javascript Logger (Christophe)
+
+Version 3.1.6 July 22, 2009
BUG: Issue#98 - Missing file in quickstart demo (Chrisotphe)
+BUG: Issue#105 - Enabling/disabling TFileUpload on TPanel (Carl)
BUG: Issue#115 - Clientside registry of serverside controls don't cover all controls: Prado.WebUI.TAutoComplete, Prado.WebUI.TInPlaceTextBox (Yves)
BUG: Issue#117 - Consider TValidationSummary.DisplayMode="HeaderOnly" if TValidationSummary.ShowMessageBox is set (Yves)
BUG: Issue#164 - CultureInfo::validCulture should be declared as a static method (Christophe)
BUG: Issue#168 - TSqlMapXmlConfiguration: CacheModel properties are not set (Yves)
BUG: Issue#172 - TDbCache fails to recognize need to re-create cache table (Yves)
+BUG: Issue#178 - TActiveFileUpload progress indicator is misleading (Christophe, googlenew-at-pcforum.hu)
ENH: Issue#174 - TErrorHandler: HTTP error messages contains sensitive information (Yves)
ENH: Issue#175 - TBulletedList: Introduce TBulletStyle::None (Yves)
ENH: Issue#176 - THttpResponse::writeFile() add additional parameters to take more influence on behavior (forceDownload, clientFileName, fileSize), add png to defaultMimeTypes (Yves)
@@ -28,6 +61,7 @@ ENH: TAssetManager: introduce protected property "Published" to allow subclasses
ENH: TFirePhpLogRoute: bypass to TBrowserLogRoute if headers already sent / php.ini (output_buffering=Off, implicit_flush=On) (Yves)
EHN: Performance optimization in PradoBase::createComponent() (Yves)
EHN: Add property TPage.EnableStateCompression and related modifications in TPageStateFormatter since it is unnecessary to compress clientstate if TCachePageStatePersister/TSessionPageStatePersister is used (Yves)
+ENH: Add property CssClass to TBrowserLog otuput and hide inline CSS (Carl)
Version 3.1.5 May 24, 2009
BUG: Issue#55 - TPropertyAccess.get and has don't recognize magic getter __get (Yves)
diff --git a/UPGRADE b/UPGRADE
index c21495d9..6d4dae73 100644
--- a/UPGRADE
+++ b/UPGRADE
@@ -9,6 +9,13 @@ if you want to upgrade from version A to version C and there is
version B between A and C, you need to following the instructions
for both A and B.
+Upgrading from v3.1.6
+---------------------
+- The different SQLMap cache engines (TSQLMapFifoCache, TSQLMapLRUCache, TSQLMapApplicationCache) doesn't
+take anymore the cache size in their constructor. Instead, they take the cachemodel object who instanciated them.
+It shouldn't affect existing code, except if you instanciate one of this cache directly (i.e, without a <cachemodel>
+directive in your SQLMap configuration)
+
Upgrading from v3.1.5
---------------------
diff --git a/buildscripts/texbuilder/quickstart/quickstart.tex b/buildscripts/texbuilder/quickstart/quickstart.tex
index 2d45f17a..dbb30179 100644
--- a/buildscripts/texbuilder/quickstart/quickstart.tex
+++ b/buildscripts/texbuilder/quickstart/quickstart.tex
@@ -52,7 +52,7 @@
%----------------- TITLE --------------
-\title{\Huge \bfseries PRADO v3.1.6 Quickstart Tutorial
+\title{\Huge \bfseries PRADO v3.2 Quickstart Tutorial
\thanks{Copyright 2004-2009. All Rights Reserved.}
}
\author{Qiang Xue and Wei Zhuo}
diff --git a/demos/quickstart/protected/pages/Controls/JavascriptLogger.page b/demos/quickstart/protected/pages/Controls/JavascriptLogger.page
index 3c7d0684..c2187632 100644
--- a/demos/quickstart/protected/pages/Controls/JavascriptLogger.page
+++ b/demos/quickstart/protected/pages/Controls/JavascriptLogger.page
@@ -4,7 +4,7 @@
<com:DocLink ClassPath="System.Web.UI.WebControls.TJavascriptLogger" />
<p id="390277" class="block-content">
-<tt>TJavascriptLogger</tt> provides logging for client-side javascript. It is mainly a wrapper of the Javascript developed at <a href="http://gleepglop.com/javascripts/logger/">http://gleepglop.com/javascripts/logger/</a>.
+<tt>TJavascriptLogger</tt> provides logging for client-side javascript. It is mainly a wrapper of the Javascript developed at <a href="http://web.archive.org/web/20060512041505/gleepglop.com/javascripts/logger/">http://gleepglop.com/javascripts/logger/</a>.
</p>
<p id="390278" class="block-content">
diff --git a/demos/quickstart/protected/pages/Controls/id/JavascriptLogger.page b/demos/quickstart/protected/pages/Controls/id/JavascriptLogger.page
index 93be7d9b..4fb52ee5 100644
--- a/demos/quickstart/protected/pages/Controls/id/JavascriptLogger.page
+++ b/demos/quickstart/protected/pages/Controls/id/JavascriptLogger.page
@@ -4,7 +4,7 @@
<com:DocLink ClassPath="System.Web.UI.WebControls.TJavascriptLogger" />
<p id="390277" class="block-content">
-<tt>TJavascriptLogger</tt> menyediakan javascript pencatatan sisi-klien. Ini sebagian besar adalah pelapis dari Javascript yang dikembangkan di <a href="http://gleepglop.com/javascripts/logger/">http://gleepglop.com/javascripts/logger/</a>.
+<tt>TJavascriptLogger</tt> menyediakan javascript pencatatan sisi-klien. Ini sebagian besar adalah pelapis dari Javascript yang dikembangkan di <a href="http://web.archive.org/web/20060512041505/gleepglop.com/javascripts/logger/">http://gleepglop.com/javascripts/logger/</a>.
</p>
<p id="390278" class="block-content">
diff --git a/framework/3rdParty/WsdlGen/Wsdl.php b/framework/3rdParty/WsdlGen/Wsdl.php
index 1df2f337..3ec5bdf1 100644
--- a/framework/3rdParty/WsdlGen/Wsdl.php
+++ b/framework/3rdParty/WsdlGen/Wsdl.php
@@ -148,15 +148,16 @@ class Wsdl
$complexType->setAttribute('name', $type);
if(substr($type, strlen($type) - 5, 5) == 'Array') // if it's an array
{
- $complexContent = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xsd:complexContent');
- $restriction = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xsd:restriction');
- $restriction->setAttribute('base', 'soap-enc:Array');
- $attribute = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xsd:attribute');
- $attribute->setAttribute('ref', "soap-enc:arrayType");
- $attribute->setAttribute('wsdl:arrayType', $this->getArrayTypePrefix($type) . substr($type, 0, strlen($type) - 5) . '[]');
- $restriction->appendChild($attribute);
- $complexContent->appendChild($restriction);
- $complexType->appendChild($complexContent);
+ $sequence = $dom->createElement("xsd:sequence");
+
+ $singularType = substr($type, 0, strlen($type) - 5);
+ $e = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xsd:element');
+ $e->setAttribute('name', $singularType);
+ $e->setAttribute('type', sprintf('tns:%s',$singularType));
+ $e->setAttribute('minOccurs','0');
+ $e->setAttribute('maxOccurs','unbounded');
+ $sequence->appendChild($e);
+ $complexType->appendChild($sequence);
}
else
{
diff --git a/framework/3rdParty/readme.html b/framework/3rdParty/readme.html
index bf9c243f..a25ec97a 100644
--- a/framework/3rdParty/readme.html
+++ b/framework/3rdParty/readme.html
@@ -99,7 +99,7 @@ projects.
</tr>
<tr>
<td><a href="../Web/Javascripts/prado/logger/logger.js">../Web/Javascripts/prado/logger/logger.js</a></td>
- <td><a href="http://gleepglop.com/javascripts/logger/">http://gleepglop.com/javascripts/logger/</a> <a href="http://slayeroffice.com">http://slayeroffice.com</a></td>
+ <td><a href="http://web.archive.org/web/20060512041505/gleepglop.com/javascripts/logger/">http://web.archive.org/web/20060512041505/gleepglop.com/javascripts/logger/</a> <a href="http://slayeroffice.com">http://slayeroffice.com</a></td>
<td>None</td>
<td>TJavascriptLogger</td>
<td>Javascript logger by Corey Johnson. Object Tree by S.G. Chipman.</td>
diff --git a/framework/Caching/TDbCache.php b/framework/Caching/TDbCache.php
index 8ea8eae9..0e013c79 100644
--- a/framework/Caching/TDbCache.php
+++ b/framework/Caching/TDbCache.php
@@ -183,7 +183,6 @@ class TDbCache extends TCache
{
if($this->_cacheInitialized && !$force) return;
$db=$this->getDbConnection();
- $db->setActive(true);
try
{
$key = 'TDbCache:' . $this->_cacheTable . ':created';
@@ -328,6 +327,8 @@ class TDbCache extends TCache
{
if($this->_db===null)
$this->_db=$this->createDbConnection();
+
+ $this->_db->setActive(true);
return $this->_db;
}
@@ -464,7 +465,7 @@ class TDbCache extends TCache
if(!$this->_cacheInitialized) $this->initializeCache();
try {
$sql='SELECT value FROM '.$this->_cacheTable.' WHERE itemkey=\''.$key.'\' AND (expire=0 OR expire>'.time().') ORDER BY expire DESC';
- $command=$this->_db->createCommand($sql);
+ $command=$this->getDbConnection()->createCommand($sql);
return $command->queryScalar();
}
catch(Exception $e)
@@ -505,7 +506,7 @@ class TDbCache extends TCache
$sql="INSERT INTO {$this->_cacheTable} (itemkey,value,expire) VALUES(:key,:value,$expire)";
try
{
- $command=$this->_db->createCommand($sql);
+ $command=$this->getDbConnection()->createCommand($sql);
$command->bindValue(':key',$key,PDO::PARAM_STR);
$command->bindValue(':value',$value,PDO::PARAM_LOB);
$command->execute();
@@ -537,7 +538,7 @@ class TDbCache extends TCache
if(!$this->_cacheInitialized) $this->initializeCache();
try
{
- $command=$this->_db->createCommand("DELETE FROM {$this->_cacheTable} WHERE itemkey=:key");
+ $command=$this->getDbConnection()->createCommand("DELETE FROM {$this->_cacheTable} WHERE itemkey=:key");
$command->bindValue(':key',$key,PDO::PARAM_STR);
$command->execute();
return true;
@@ -559,7 +560,7 @@ class TDbCache extends TCache
if(!$this->_cacheInitialized) $this->initializeCache();
try
{
- $command = $this->_db->createCommand("DELETE FROM {$this->_cacheTable}");
+ $command = $this->getDbConnection()->createCommand("DELETE FROM {$this->_cacheTable}");
$command->execute();
}
catch(Exception $e)
diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php
index a3daf35c..7e584514 100644
--- a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php
+++ b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php
@@ -4,7 +4,7 @@
*
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2008 PradoSoft
+ * @copyright Copyright &copy; 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Data.ActiveRecord.Relations
@@ -85,6 +85,8 @@ abstract class TActiveRecordRelation
}
else if($results instanceof TActiveRecordRelation)
$stack[] = $this; //call it later
+ else if($results === null || !$validArray)
+ $stacks=array();
return $results;
}
diff --git a/framework/Data/Common/Mysql/TMysqlMetaData.php b/framework/Data/Common/Mysql/TMysqlMetaData.php
index ae552cf3..fad33cea 100644
--- a/framework/Data/Common/Mysql/TMysqlMetaData.php
+++ b/framework/Data/Common/Mysql/TMysqlMetaData.php
@@ -4,7 +4,7 @@
*
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2008 PradoSoft
+ * @copyright Copyright &copy; 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Data.Common.Mysql
@@ -110,7 +110,7 @@ class TMysqlMetaData extends TDbMetaData
//find SET/ENUM values
if($this->isEnumSetType($info['DbType']))
- $info['DbTypeValues'] = preg_split('/\s*,\s*|\s+/', preg_replace('/\'|"/', '', $match[1]));
+ $info['DbTypeValues'] = preg_split("/[',]/S", $match[1], -1, PREG_SPLIT_NO_EMPTY);
//find column size, precision and scale
$pscale = array();
@@ -212,9 +212,9 @@ class TMysqlMetaData extends TDbMetaData
if($this->getServerVersion()<5.01)
return false;
if($schemaName!==null)
- $sql = "SHOW FULL TABLES FROM `{$schemaName}` LIKE ':table'";
+ $sql = "SHOW FULL TABLES FROM `{$schemaName}` LIKE :table";
else
- $sql = "SHOW FULL TABLES LIKE ':table'";
+ $sql = "SHOW FULL TABLES LIKE :table";
$command = $this->getDbConnection()->createCommand($sql);
$command->bindValue(':table', $tableName);
@@ -246,8 +246,8 @@ class TMysqlMetaData extends TDbMetaData
if($row['Key_name']==='PRIMARY')
$primary[] = $row['Column_name'];
}
- // MySQL version was increased to >=5.1.21 instead of 5.x
- // due to a MySQL bug (http://bugs.mysql.com/bug.php?id=19588)
+ // MySQL version was increased to >=5.1.21 instead of 5.x
+ // due to a MySQL bug (http://bugs.mysql.com/bug.php?id=19588)
if($this->getServerVersion() >= 5.121)
$foreign = $this->getForeignConstraints($schemaName,$tableName);
else
@@ -353,4 +353,4 @@ EOD;
return false;
}
}
-
+
diff --git a/framework/Data/Common/TDbCommandBuilder.php b/framework/Data/Common/TDbCommandBuilder.php
index 155a62f8..0dc13e7e 100644
--- a/framework/Data/Common/TDbCommandBuilder.php
+++ b/framework/Data/Common/TDbCommandBuilder.php
@@ -157,16 +157,158 @@ class TDbCommandBuilder extends TComponent
}
/**
+ *
+ * Different behavior depends on type of passed data
+ * string
+ * usage without modification
+ *
+ * null
+ * will be expanded to full list of quoted table column names (quoting depends on database)
+ *
+ * array
+ * - Column names will be quoted if used as key or value of array
+ * <code>
+ * array('col1', 'col2', 'col2')
+ * // SELECT `col1`, `col2`, `col3` FROM...
+ * </code>
+ *
+ * - Column aliasing
+ * <code>
+ * array('mycol1' => 'col1', 'mycol2' => 'COUNT(*)')
+ * // SELECT `col1` AS mycol1, COUNT(*) AS mycol2 FROM...
+ * </code>
+ *
+ * - NULL and scalar values (strings will be quoted depending on database)
+ * <code>
+ * array('col1' => 'my custom string', 'col2' => 1.0, 'col3' => 'NULL')
+ * // SELECT "my custom string" AS `col1`, 1.0 AS `col2`, NULL AS `col3` FROM...
+ * </code>
+ *
+ * - If the *-wildcard char is used as key or value, add the full list of quoted table column names
+ * <code>
+ * array('col1' => 'NULL', '*')
+ * // SELECT `col1`, `col2`, `col3`, NULL AS `col1` FROM...
+ * </code>
+ * @param mixed $value
+ * @return array of generated fields - use implode(', ', $selectfieldlist) to collapse field list for usage
+ * @since 3.1.7
+ * @todo add support for table aliasing
+ * @todo add support for quoting of column aliasing
+ */
+ public function getSelectFieldList($data='*') {
+ if(is_scalar($data)) {
+ $tmp = explode(',', $data);
+ $result = array();
+ foreach($tmp as $v)
+ $result[] = trim($v);
+ return $result;
+ }
+
+ $bHasWildcard = false;
+ $result = array();
+ if(is_array($data) || $data instanceof Traversable) {
+ $columns = $this->getTableInfo()->getColumns();
+ foreach($data as $key=>$value) {
+ if($key==='*' || $value==='*') {
+ $bHasWildcard = true;
+ continue;
+ }
+
+ if(strToUpper($key)==='NULL') {
+ $result[] = 'NULL';
+ continue;
+ }
+
+ if(strpos($key, '(')!==false && strpos($key, ')')!==false) {
+ $result[] = $key;
+ continue;
+ }
+
+ if(stripos($key, 'AS')!==false) {
+ $result[] = $key;
+ continue;
+ }
+
+ if(stripos($value, 'AS')!==false) {
+ $result[] = $value;
+ continue;
+ }
+
+ $v = isset($columns[$value]);
+ $k = isset($columns[$key]);
+ if(is_integer($key) && $v) {
+ $key = $value;
+ $k = $v;
+ }
+
+ if(strToUpper($value)==='NULL') {
+ if($k)
+ $result[] = 'NULL AS ' . $columns[$key]->getColumnName();
+ else
+ $result[] = 'NULL' . (is_string($key) ? (' AS ' . (string)$key) : '');
+ continue;
+ }
+
+ if(strpos($value, '(')!==false && strpos($value, ')')!==false) {
+ if($k)
+ $result[] = $value . ' AS ' . $columns[$key]->getColumnName();
+ else
+ $result[] = $value . (is_string($key) ? (' AS ' . (string)$key) : '');
+ continue;
+ }
+
+ if($v && $key==$value) {
+ $result[] = $columns[$value]->getColumnName();
+ continue;
+ }
+
+ if($k && $value==null) {
+ $result[] = $columns[$key]->getColumnName();
+ continue;
+ }
+
+ if(is_string($key) && $v) {
+ $result[] = $columns[$value]->getColumnName() . ' AS ' . $key;
+ continue;
+ }
+
+ if(is_numeric($value) && $k) {
+ $result[] = $value . ' AS ' . $columns[$key]->getColumnName();
+ continue;
+ }
+
+ if(is_string($value) && $k) {
+ $result[] = $this->getDbConnection()->quoteString($value) . ' AS ' . $columns[$key]->getColumnName();
+ continue;
+ }
+
+ if(!$v && !$k && is_integer($key)) {
+ $result[] = is_numeric($value) ? $value : $this->getDbConnection()->quoteString((string)$value);
+ continue;
+ }
+
+ $result[] = (is_numeric($value) ? $value : $this->getDbConnection()->quoteString((string)$value)) . ' AS ' . $key;
+ }
+ }
+
+ if($data===null || count($result) == 0 || $bHasWildcard)
+ $result = $result = array_merge($this->getTableInfo()->getColumnNames(), $result);
+
+ return $result;
+ }
+
+ /**
* Appends the $where condition to the string "SELECT * FROM tableName WHERE ".
* The tableName is obtained from the {@link setTableInfo TableInfo} property.
* @param string query condition
* @param array condition parameters.
* @return TDbCommand query command.
*/
- public function createFindCommand($where='1=1', $parameters=array(), $ordering=array(), $limit=-1, $offset=-1)
+ public function createFindCommand($where='1=1', $parameters=array(), $ordering=array(), $limit=-1, $offset=-1, $select='*')
{
$table = $this->getTableInfo()->getTableFullName();
- $sql = "SELECT * FROM {$table}";
+ $fields = implode(', ', $this -> getSelectFieldList($select));
+ $sql = "SELECT {$fields} FROM {$table}";
if(!empty($where))
$sql .= " WHERE {$where}";
return $this->applyCriterias($sql, $parameters, $ordering, $limit, $offset);
@@ -191,11 +333,7 @@ class TDbCommandBuilder extends TComponent
*/
public function createCountCommand($where='1=1', $parameters=array(),$ordering=array(), $limit=-1, $offset=-1)
{
- $table = $this->getTableInfo()->getTableFullName();
- $sql = "SELECT COUNT(*) FROM {$table}";
- if(!empty($where))
- $sql .= " WHERE {$where}";
- return $this->applyCriterias($sql, $parameters, $ordering, $limit, $offset);
+ return $this->createFindCommand($where, $parameters, $ordering, $limit, $offset, 'COUNT(*)');
}
/**
@@ -368,4 +506,4 @@ class TDbCommandBuilder extends TComponent
}
}
-?>
+?> \ No newline at end of file
diff --git a/framework/Data/Common/TDbTableInfo.php b/framework/Data/Common/TDbTableInfo.php
index e2aae3d0..455dbc33 100644
--- a/framework/Data/Common/TDbTableInfo.php
+++ b/framework/Data/Common/TDbTableInfo.php
@@ -27,7 +27,13 @@ class TDbTableInfo extends TComponent
private $_columns;
- private $_lowercase;
+ private $_lowercase;
+
+ /**
+ * @var null|array
+ * @since 3.1.7
+ */
+ private $_names = null;
/**
* Sets the database table meta data information.
@@ -118,11 +124,14 @@ class TDbTableInfo extends TComponent
* @return array table column names (identifier quoted)
*/
public function getColumnNames()
- {
- $names=array();
- foreach($this->getColumns() as $column)
- $names[] = $column->getColumnName();
- return $names;
+ {
+ if($this->_names===null)
+ {
+ $this->_names=array();
+ foreach($this->getColumns() as $column)
+ $this->_names[] = $column->getColumnName();
+ }
+ return $this->_names;
}
/**
@@ -154,5 +163,4 @@ class TDbTableInfo extends TComponent
}
return $this->_lowercase;
}
-}
-
+} \ No newline at end of file
diff --git a/framework/Data/DataGateway/TDataGatewayCommand.php b/framework/Data/DataGateway/TDataGatewayCommand.php
index 7425e6c4..e290f457 100644
--- a/framework/Data/DataGateway/TDataGatewayCommand.php
+++ b/framework/Data/DataGateway/TDataGatewayCommand.php
@@ -148,7 +148,8 @@ class TDataGatewayCommand extends TComponent
$ordering = $criteria->getOrdersBy();
$limit = $criteria->getLimit();
$offset = $criteria->getOffset();
- $command = $this->getBuilder()->createFindCommand($where,$parameters,$ordering,$limit,$offset);
+ $select = $criteria->getSelect();
+ $command = $this->getBuilder()->createFindCommand($where,$parameters,$ordering,$limit,$offset,$select);
$this->onCreateCommand($command, $criteria);
return $command;
}
@@ -232,7 +233,7 @@ class TDataGatewayCommand extends TComponent
throw new TDbException('dbtablegateway_missing_pk_values',
$this->getTableInfo()->getTableFullName());
}
- if($count>1 && !is_array($values[0]))
+ if($count>1 && (!isset($values[0]) || !is_array($values[0])))
$values = array($values);
if($count > 1 && count($values[0]) !== $count)
{
diff --git a/framework/Data/DataGateway/TSqlCriteria.php b/framework/Data/DataGateway/TSqlCriteria.php
index 4ebdeb48..14e37b35 100644
--- a/framework/Data/DataGateway/TSqlCriteria.php
+++ b/framework/Data/DataGateway/TSqlCriteria.php
@@ -31,6 +31,11 @@
*/
class TSqlCriteria extends TComponent
{
+ /**
+ * @var mixed
+ * @since 3.1.7
+ */
+ private $_select='*';
private $_condition;
private $_parameters;
private $_ordersBy;
@@ -56,6 +61,60 @@ class TSqlCriteria extends TComponent
}
/**
+ * Gets the field list to be placed after the SELECT in the SQL. Default to '*'
+ * @return mixed
+ * @since 3.1.7
+ */
+ public function getSelect()
+ {
+ return $this->_select;
+ }
+
+ /**
+ * Sets the field list to be placed after the SELECT in the SQL.
+ *
+ * Different behavior depends on type of assigned value
+ * string
+ * usage without modification
+ *
+ * null
+ * will be expanded to full list of quoted table column names (quoting depends on database)
+ *
+ * array
+ * - Column names will be quoted if used as key or value of array
+ * <code>
+ * array('col1', 'col2', 'col2')
+ * // SELECT `col1`, `col2`, `col3` FROM...
+ * </code>
+ *
+ * - Column aliasing
+ * <code>
+ * array('mycol1' => 'col1', 'mycol2' => 'COUNT(*)')
+ * // SELECT `col1` AS mycol1, COUNT(*) AS mycol2 FROM...
+ * </code>
+ *
+ * - NULL and scalar values (strings will be quoted depending on database)
+ * <code>
+ * array('col1' => 'my custom string', 'col2' => 1.0, 'col3' => 'NULL')
+ * // SELECT "my custom string" AS `col1`, 1.0 AS `col2`, NULL AS `col3` FROM...
+ * </code>
+ *
+ * - If the *-wildcard char is used as key or value, add the full list of quoted table column names
+ * <code>
+ * array('col1' => 'NULL', '*')
+ * // SELECT `col1`, `col2`, `col3`, NULL AS `col1` FROM...
+ * </code>
+ *
+ * @param mixed
+ * @since 3.1.7
+ * @see TDbCommandBuilder::getSelectFieldList()
+ */
+ public function setSelect($value)
+ {
+ $this->_select = $value;
+ }
+
+ /**
* @return string search conditions.
*/
public function getCondition()
@@ -68,17 +127,17 @@ class TSqlCriteria extends TComponent
* @param string search conditions.
*/
public function setCondition($value)
- {
+ {
if(empty($value)) {
return;
}
-
+
// supporting the following SELECT-syntax:
// [ORDER BY {col_name | expr | position}
// [ASC | DESC], ...]
// [LIMIT {[offset,] row_count | row_count OFFSET offset}]
// See: http://dev.mysql.com/doc/refman/5.0/en/select.html
-
+
if(preg_match('/ORDER\s+BY\s+(.*?)(?=LIMIT)|ORDER\s+BY\s+(.*?)$/i', $value, $matches) > 0) {
// condition contains ORDER BY
$value = str_replace($matches[0], '', $value);
@@ -88,7 +147,7 @@ class TSqlCriteria extends TComponent
$this->setOrdersBy($matches[2]);
}
}
-
+
if(preg_match('/LIMIT\s+([\d\s,]+)/i', $value, $matches) > 0) {
// condition contains limit
$value = str_replace($matches[0], '', $value); // remove limit from query
@@ -106,7 +165,7 @@ class TSqlCriteria extends TComponent
$value = str_replace($matches[0], '', $value); // remove offset from query
$this->_offset = (int)$matches[1]; // set offset in criteria
}
-
+
$this->_condition = trim($value);
}
@@ -222,5 +281,4 @@ class TSqlCriteria extends TComponent
return $str;
}
}
-
-?>
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php b/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php
index d85148eb..540a3acd 100644
--- a/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php
+++ b/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php
@@ -97,7 +97,7 @@ class TSqlMapCacheModel extends TComponent
public function initialize($cache=null)
{
if($cache===null)
- $this->_cache= Prado::createComponent($this->getImplementationClass());
+ $this->_cache= Prado::createComponent($this->getImplementationClass(), $this);
else
$this->_cache=$cache;
}
diff --git a/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php b/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php
index f6e0acd5..c49a4219 100644
--- a/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php
+++ b/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php
@@ -710,7 +710,7 @@ class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder
if(in_array(strtolower($name), $properties))
$cacheModel->{'set'.$name}((string)$value);
}
- $cache = Prado::createComponent($cacheModel->getImplementationClass());
+ $cache = Prado::createComponent($cacheModel->getImplementationClass(), $cacheModel);
$this->setObjectPropFromNode($cache,$node,$properties);
foreach($node->xpath('property') as $propertyNode)
diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapCache.php b/framework/Data/SqlMap/DataMapper/TSqlMapCache.php
index 05b72e08..5262bdf8 100644
--- a/framework/Data/SqlMap/DataMapper/TSqlMapCache.php
+++ b/framework/Data/SqlMap/DataMapper/TSqlMapCache.php
@@ -4,7 +4,7 @@
*
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2008 PradoSoft
+ * @copyright Copyright &copy; 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Data.SqlMap
@@ -25,16 +25,17 @@ abstract class TSqlMapCache implements ICache
protected $_keyList;
protected $_cache;
protected $_cacheSize = 100;
+ protected $_cacheModel = null;
/**
* Create a new cache with limited cache size.
- * @param integer maxium number of items to cache.
+ * @param TSqlMapCacheModel $cacheModel.
*/
- public function __construct($cacheSize=100)
+ public function __construct($cacheModel=null)
{
$this->_cache = new TMap;
- $this->_cacheSize = intval($cacheSize);
$this->_keyList = new TList;
+ $this->_cacheModel=$cacheModel;
}
/**
@@ -173,20 +174,71 @@ class TSqlMapLruCache extends TSqlMapCache
*/
class TSqlMapApplicationCache implements ICache
{
+ protected $_cacheModel=null;
+
+ /**
+ * Create a new cache with limited cache size.
+ * @param TSqlMapCacheModel $cacheModel.
+ */
+ public function __construct($cacheModel=null)
+ {
+ $this->_cacheModel=$cacheModel;
+ }
+
+ /**
+ *
+ * @return string a KeyListID for the cache model.
+ */
+ protected function getKeyListId()
+ {
+ $id='keyList';
+ if ($this->_cacheModel instanceof TSqlMapCacheModel)
+ $id.='_'.$this->_cacheModel->getId();
+ return $id;
+ }
+ /**
+ * Retreive keylist from cache or create it if it doesn't exists
+ * @return TList
+ */
+ protected function getKeyList()
+ {
+ if (($keyList=$this->getCache()->get($this->getKeyListId()))===false)
+ {
+ $keyList=new TList();
+ $this->getCache()->set($this->getKeyListId(), $keyList);
+ }
+ return $keyList;
+ }
+
+ protected function setKeyList($keyList)
+ {
+ $this->getCache()->set($this->getKeyListId(), $keyList);
+ }
+
/**
* @param string item to be deleted.
*/
public function delete($key)
{
+ $keyList=$this->getKeyList();
+ $keyList->remove($key);
$this->getCache()->delete($key);
+ $this->setKeyList($keyList);
}
/**
- * Deletes all items in the cache.
+ * Deletes all items in the cache, only for data cached by sqlmap cachemodel
*/
public function flush()
{
- $this->getCache()->flush();
+ $keyList=$this->getKeyList();
+ $cache=$this->getCache();
+ foreach ($keyList as $key)
+ {
+ $cache->delete($key);
+ }
+ // Remove the old keylist
+ $cache->delete($this->getKeyListId());
}
/**
@@ -195,6 +247,16 @@ class TSqlMapApplicationCache implements ICache
public function get($key)
{
$result = $this->getCache()->get($key);
+ if ($result === false)
+ {
+ // if the key has not been found in cache (e.g expired), remove from keylist
+ $keyList=$this->getKeyList();
+ if ($keyList->contains($key))
+ {
+ $keyList->remove($key);
+ $this->setKeyList($keyList);
+ }
+ }
return $result === false ? null : $result;
}
@@ -206,6 +268,12 @@ class TSqlMapApplicationCache implements ICache
public function set($key, $value,$expire=0,$dependency=null)
{
$this->getCache()->set($key, $value, $expire,$dependency);
+ $keyList=$this->getKeyList();
+ if (!$keyList->contains($key))
+ {
+ $keyList->add($key);
+ $this->setKeyList($keyList);
+ }
}
/**
diff --git a/framework/Data/SqlMap/TSqlMapConfig.php b/framework/Data/SqlMap/TSqlMapConfig.php
index c57ab40e..c5f06ab8 100644
--- a/framework/Data/SqlMap/TSqlMapConfig.php
+++ b/framework/Data/SqlMap/TSqlMapConfig.php
@@ -53,6 +53,31 @@ class TSqlMapConfig extends TDataSourceConfig
}
/**
+ * Create and configure the data mapper using sqlmap configuration file.
+ * Or if cache is enabled and manager already cached load from cache.
+ * If cache is enabled, the data mapper instance is cached.
+ *
+ * @return TSqlMapManager SqlMap manager instance
+ * @since 3.1.7
+ */
+ public function getSqlMapManager() {
+ Prado::using('System.Data.SqlMap.TSqlMapManager');
+ if(($manager = $this->loadCachedSqlMapManager())===null)
+ {
+ $manager = new TSqlMapManager($this->getDbConnection());
+ if(strlen($file=$this->getConfigFile()) > 0)
+ {
+ $manager->configureXml($file);
+ $this->cacheSqlMapManager($manager);
+ }
+ }
+ elseif($this->getConnectionID() !== '') {
+ $manager->setDbConnection($this->getDbConnection());
+ }
+ return $manager;
+ }
+
+ /**
* Saves the current SqlMap manager to cache.
* @return boolean true if SqlMap manager was cached, false otherwise.
*/
@@ -87,6 +112,7 @@ class TSqlMapConfig extends TDataSourceConfig
return $manager;
}
}
+ return null;
}
/**
@@ -134,26 +160,11 @@ class TSqlMapConfig extends TDataSourceConfig
}
/**
- * Configure the data mapper using sqlmap configuration file.
- * If cache is enabled, the data mapper instance is cached.
* @return TSqlMapGateway SqlMap gateway instance.
*/
protected function createSqlMapGateway()
{
- Prado::using('System.Data.SqlMap.TSqlMapManager');
- if(($manager = $this->loadCachedSqlMapManager())===null)
- {
- $manager = new TSqlMapManager($this->getDbConnection());
- if(strlen($file=$this->getConfigFile()) > 0)
- {
- $manager->configureXml($file);
- $this->cacheSqlMapManager($manager);
- }
- }
- else {
- $manager->setDbConnection($this->getDbConnection());
- }
- return $manager->getSqlmapGateway();
+ return $this->getSqlMapManager()->getSqlmapGateway();
}
/**
diff --git a/framework/Data/TDbConnection.php b/framework/Data/TDbConnection.php
index 26d61883..4726083c 100644
--- a/framework/Data/TDbConnection.php
+++ b/framework/Data/TDbConnection.php
@@ -27,10 +27,10 @@ Prado::using('System.Data.TDbCommand');
* specifying {@link setConnectionString ConnectionString}, {@link setUsername Username}
* and {@link setPassword Password}.
*
- * Since 3.1.2, the connection charset can be set (for MySQL and PostgreSQL databases only)
+ * Since 3.1.2, the connection charset can be set (for MySQL and PostgreSQL databases only)
* using the {@link setCharset Charset} property. The value of this property is database dependant.
- * e.g. for mysql, you can use 'latin1' for cp1252 West European, 'utf8' for unicode, ...
- *
+ * e.g. for mysql, you can use 'latin1' for cp1252 West European, 'utf8' for unicode, ...
+ *
* The following example shows how to create a TDbConnection instance and establish
* the actual connection:
* <code>
@@ -83,6 +83,12 @@ Prado::using('System.Data.TDbCommand');
*/
class TDbConnection extends TComponent
{
+ /**
+ *
+ * @since 3.1.7
+ */
+ const DEFAULT_TRANSACTION_CLASS = 'System.Data.TDbTransaction';
+
private $_dsn='';
private $_username='';
private $_password='';
@@ -93,12 +99,18 @@ class TDbConnection extends TComponent
private $_transaction;
/**
+ * @var string
+ * @since 3.1.7
+ */
+ private $_transactionClass=self::DEFAULT_TRANSACTION_CLASS;
+
+ /**
* Constructor.
* Note, the DB connection is not established when this connection
* instance is created. Set {@link setActive Active} property to true
* to establish the connection.
* Since 3.1.2, you can set the charset for MySql connection
- *
+ *
* @param string The Data Source Name, or DSN, contains the information required to connect to the database.
* @param string The user name for the DSN string.
* @param string The password for the DSN string.
@@ -168,7 +180,7 @@ class TDbConnection extends TComponent
{
$this->_pdo=new PDO($this->getConnectionString(),$this->getUsername(),
$this->getPassword(),$this->_attributes);
- // This attribute is only useful for PDO::MySql driver.
+ // This attribute is only useful for PDO::MySql driver.
// Ignore the warning if a driver doesn't understand this.
@$this->_pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
$this->_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
@@ -194,7 +206,7 @@ class TDbConnection extends TComponent
/*
* Set the database connection charset.
- * Only MySql databases are supported for now.
+ * Only MySql databases are supported for now.
* @since 3.1.2
*/
protected function setConnectionCharset()
@@ -204,7 +216,7 @@ class TDbConnection extends TComponent
switch ($this->_pdo->getAttribute(PDO::ATTR_DRIVER_NAME))
{
case 'mysql':
- $stmt = $this->_pdo->prepare('SET CHARACTER SET ?');
+ $stmt = $this->_pdo->prepare('SET NAMES ?');
break;
case 'pgsql':
$stmt = $this->_pdo->prepare('SET client_encoding TO ?');
@@ -212,7 +224,7 @@ class TDbConnection extends TComponent
}
$stmt->execute(array($this->_charset));
}
-
+
/**
* @return string The Data Source Name, or DSN, contains the information required to connect to the database.
*/
@@ -269,7 +281,7 @@ class TDbConnection extends TComponent
{
return $this->_charset;
}
-
+
/**
* @param string the charset used for database connection
*/
@@ -278,7 +290,7 @@ class TDbConnection extends TComponent
$this->_charset=$value;
$this->setConnectionCharset();
}
-
+
/**
* @return PDO the PDO instance, null if the connection is not established yet
*/
@@ -324,13 +336,32 @@ class TDbConnection extends TComponent
if($this->getActive())
{
$this->_pdo->beginTransaction();
- return $this->_transaction=new TDbTransaction($this);
+ return $this->_transaction=Prado::createComponent($this->getTransactionClass(), $this);
}
else
throw new TDbException('dbconnection_connection_inactive');
}
/**
+ * @return string Transaction class name to be created by calling {@link TDbConnection::beginTransaction}. Defaults to 'System.Data.TDbTransaction'.
+ * @since 3.1.7
+ */
+ public function getTransactionClass()
+ {
+ return $this->_transactionClass;
+ }
+
+
+ /**
+ * @param string Transaction class name to be created by calling {@link TDbConnection::beginTransaction}.
+ * @since 3.1.7
+ */
+ public function setTransactionClass($value)
+ {
+ $this->_transactionClass = (string)$value;
+ }
+
+ /**
* Returns the ID of the last inserted row or sequence value.
* @param string name of the sequence object (required by some DBMS)
* @return string the row ID of the last row inserted, or the last value retrieved from the sequence object
diff --git a/framework/Exceptions/messages/messages-fr.txt b/framework/Exceptions/messages/messages-fr.txt
index 82507b1a..5dce3812 100644
--- a/framework/Exceptions/messages/messages-fr.txt
+++ b/framework/Exceptions/messages/messages-fr.txt
@@ -71,6 +71,7 @@ httpsession_autostart_unchangeable = THttpSession.AutoStart ne peut pas être m
httpsession_gcprobability_unchangeable = THttpSession.GCProbability ne peut pas être modifié après que la session ait démarré.
httpsession_gcprobability_invalid = THttpSession.GCProbability doit être un entier compris entre 0 et 100.
httpsession_transid_unchangeable = THttpSession.UseTransparentSessionID ne peut pas être modifié après que la session ait démarré.
+httpsession_transid_cookieonly = THttpSession.UseTransparentSessionID ne peut pas être utilisé quand THttpSession.CookieMode est fixé à "Only".
httpsession_maxlifetime_unchangeable = THttpSession.Timeout ne peut pas être modifié après que la session ait démarré.
assetmanager_basepath_invalid = TAssetManager.BasePath '{0}' est invalide. Vérifier qu'il est bien au format 'namespace' et qu'il pointe bien vers un répertoire accessible en écriture par le propriétaire du processus serveur Web
diff --git a/framework/Exceptions/messages/messages-id.txt b/framework/Exceptions/messages/messages-id.txt
index fb57a5ba..61699ddd 100644
--- a/framework/Exceptions/messages/messages-id.txt
+++ b/framework/Exceptions/messages/messages-id.txt
@@ -71,6 +71,7 @@ httpsession_autostart_unchangeable = THttpSession.AutoStart tidak bisa diubah se
httpsession_gcprobability_unchangeable = THttpSession.GCProbability tidak bisa diubah setelah sesi dimulai.
httpsession_gcprobability_invalid = THttpSession.GCProbability harus integer antara 0 dan 100.
httpsession_transid_unchangeable = THttpSession.UseTransparentSessionID tidak bisa diubah setelah sesi dimulai.
+httpsession_transid_cookieonly = THttpSession.UseTransparentSessionID cannot be set when THttpSession.CookieMode is set to Only.
httpsession_maxlifetime_unchangeable = THttpSession.Timeout tidak bisa diubah setelah sesi dimulai.
assetmanager_basepath_invalid = TAssetManager.BasePath '{0}' tidak benar. Pastikan ia dalam bentuk namespace dan mengarah ke direktori yang bisa ditulis oleh proses server Web.
diff --git a/framework/Exceptions/messages/messages-zh.txt b/framework/Exceptions/messages/messages-zh.txt
index d9a61083..1859aa92 100644
--- a/framework/Exceptions/messages/messages-zh.txt
+++ b/framework/Exceptions/messages/messages-zh.txt
@@ -76,6 +76,7 @@ httpsession_autostart_unchangeable = THttpSession.AutoStart无法被修改,å›
httpsession_gcprobability_unchangeable = THttpSession.GCProbability无法被修改,因为sessionå·²ç»å¯åŠ¨äº†ã€‚
httpsession_gcprobability_invalid = THttpSession.GCProbability必须是个0到100之间的整数。
httpsession_transid_unchangeable = THttpSession.UseTransparentSessionID无法被修改,因为sessionå·²ç»å¯åŠ¨äº†ã€‚
+httpsession_transid_cookieonly = THttpSession.UseTransparentSessionID cannot be set when THttpSession.CookieMode is set to Only.
httpsession_maxlifetime_unchangeable = THttpSession.Timeout无法被修改,因为sessionå·²ç»å¯åŠ¨äº†ã€‚
assetmanager_basepath_invalid = TAssetManager.BasePath所指路径“{0}â€éžæ³•ã€‚请确认它以命å空间方å¼æŒ‡å®šï¼Œå¹¶ä¸”它所对应的文件目录å¯ä»¥è¢«WebæœåŠ¡å™¨è¿›ç¨‹å†™å…¥ã€‚
diff --git a/framework/Exceptions/messages/messages.txt b/framework/Exceptions/messages/messages.txt
index 3bd6bbb1..402b3252 100644
--- a/framework/Exceptions/messages/messages.txt
+++ b/framework/Exceptions/messages/messages.txt
@@ -76,6 +76,7 @@ httpsession_autostart_unchangeable = THttpSession.AutoStart cannot be modified
httpsession_gcprobability_unchangeable = THttpSession.GCProbability cannot be modified after the session is started.
httpsession_gcprobability_invalid = THttpSession.GCProbability must be an integer between 0 and 100.
httpsession_transid_unchangeable = THttpSession.UseTransparentSessionID cannot be modified after the session is started.
+httpsession_transid_cookieonly = THttpSession.UseTransparentSessionID cannot be set when THttpSession.CookieMode is set to Only.
httpsession_maxlifetime_unchangeable = THttpSession.Timeout cannot be modified after the session is started.
assetmanager_basepath_invalid = TAssetManager.BasePath '{0}' is invalid. Make sure it is in namespace form and points to a directory writable by the Web server process.
diff --git a/framework/TShellApplication.php b/framework/TShellApplication.php
index 0d2cb826..e8560abf 100644
--- a/framework/TShellApplication.php
+++ b/framework/TShellApplication.php
@@ -4,7 +4,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2008 PradoSoft
+ * @copyright Copyright &copy; 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System
@@ -35,6 +35,14 @@
*/
class TShellApplication extends TApplication
{
+
+ /**
+ * Override parent implementation. TShellApplication doesn't need to start any service
+ */
+ public function startService($serviceID)
+ {
+
+ }
/**
* Runs the application.
* This method overrides the parent implementation by initializing
diff --git a/framework/Util/TLogRouter.php b/framework/Util/TLogRouter.php
index a8f42b56..c944e17d 100644
--- a/framework/Util/TLogRouter.php
+++ b/framework/Util/TLogRouter.php
@@ -635,6 +635,11 @@ class TEmailLogRoute extends TLogRoute
*/
class TBrowserLogRoute extends TLogRoute
{
+ /**
+ * @var string css class for indentifying the table structure in the dom tree
+ */
+ private $_cssClass=null;
+
public function processLogs($logs)
{
if(empty($logs) || $this->getApplication()->getMode()==='Performance') return;
@@ -659,10 +664,43 @@ class TBrowserLogRoute extends TLogRoute
}
$response->write($this->renderFooter());
}
+
+ /**
+ * @param string the css class of the control
+ */
+ public function setCssClass($value)
+ {
+ $this->_cssClass = TPropertyValue::ensureString($value);
+ }
+
+ /**
+ * @return string the css class of the control
+ */
+ public function getCssClass()
+ {
+ return TPropertyValue::ensureString($this->_cssClass);
+ }
protected function renderHeader()
{
- $string = <<<EOD
+ $string = '';
+ if($className=$this->getCssClass())
+ {
+ $string = <<<EOD
+<table class="$className">
+ <tr class="header">
+ <th colspan="5">
+ Application Log
+ </th>
+ </tr><tr class="description">
+ <th>&nbsp;</th>
+ <th>Category</th><th>Message</th><th>Time Spent (s)</th><th>Cumulated Time Spent (s)</th>
+ </tr>
+EOD;
+ }
+ else
+ {
+ $string = <<<EOD
<table cellspacing="0" cellpadding="2" border="0" width="100%" style="table-layout:auto">
<tr>
<th style="background-color: black; color:white;" colspan="5">
@@ -673,18 +711,36 @@ class TBrowserLogRoute extends TLogRoute
<th style="width: auto">Category</th><th style="width: auto">Message</th><th style="width: 120px">Time Spent (s)</th><th style="width: 150px">Cumulated Time Spent (s)</th>
</tr>
EOD;
+ }
return $string;
}
protected function renderMessage($log, $info)
{
- $bgcolor = $info['even'] ? "#fff" : "#eee";
+ $string = '';
$total = sprintf('%0.6f', $info['total']);
$delta = sprintf('%0.6f', $info['delta']);
- $color = $this->getColorLevel($log[1]);
$msg = preg_replace('/\(line[^\)]+\)$/','',$log[0]); //remove line number info
$msg = THttpUtility::htmlEncode($msg);
- $string = <<<EOD
+ if($this->getCssClass())
+ {
+ $colorCssClass = $log[1];
+ $messageCssClass = $info['even'] ? 'even' : 'odd';
+ $string = <<<EOD
+ <tr class="message level$colorCssClass $messageCssClass">
+ <td class="code">&nbsp;</td>
+ <td class="category">{$log[2]}</td>
+ <td class="message">{$msg}</td>
+ <td class="time">{$delta}</td>
+ <td class="cumulatedtime">{$total}</td>
+ </tr>
+EOD;
+ }
+ else
+ {
+ $bgcolor = $info['even'] ? "#fff" : "#eee";
+ $color = $this->getColorLevel($log[1]);
+ $string = <<<EOD
<tr style="background-color: {$bgcolor}; color:#000">
<td style="border:1px solid silver;background-color: $color;">&nbsp;</td>
<td>{$log[2]}</td>
@@ -693,6 +749,7 @@ EOD;
<td style="text-align:center">{$total}</td>
</tr>
EOD;
+ }
return $string;
}
@@ -713,13 +770,25 @@ EOD;
protected function renderFooter()
{
- $string = "<tr><td colspan=\"5\" style=\"text-align:center; background-color:black; border-top: 1px solid #ccc; padding:0.2em;\">";
- foreach(self::$_levelValues as $name => $level)
+ $string = '';
+ if($this->getCssClass())
{
- $string .= "<span style=\"color:white; border:1px solid white; background-color:".$this->getColorLevel($level);
- $string .= ";margin: 0.5em; padding:0.01em;\">".strtoupper($name)."</span>";
+ $string .= '<tr class="footer"><td colspan="5">';
+ foreach(self::$_levelValues as $name => $level)
+ {
+ $string .= '<span class="level'.$level.'">'.strtoupper($name)."</span>";
+ }
+ }
+ else
+ {
+ $string .= "<tr><td colspan=\"5\" style=\"text-align:center; background-color:black; border-top: 1px solid #ccc; padding:0.2em;\">";
+ foreach(self::$_levelValues as $name => $level)
+ {
+ $string .= "<span style=\"color:white; border:1px solid white; background-color:".$this->getColorLevel($level);
+ $string .= ";margin: 0.5em; padding:0.01em;\">".strtoupper($name)."</span>";
+ }
}
- $string .= "</td></tr></table>";
+ $string .= '</td></tr></table>';
return $string;
}
}
diff --git a/framework/Web/Javascripts/source/prado/activecontrols/activedatepicker.js b/framework/Web/Javascripts/source/prado/activecontrols/activedatepicker.js
index 3aacda21..e96b63ec 100755
--- a/framework/Web/Javascripts/source/prado/activecontrols/activedatepicker.js
+++ b/framework/Web/Javascripts/source/prado/activecontrols/activedatepicker.js
@@ -46,9 +46,9 @@ Prado.WebUI.TActiveDatePicker = Class.extend(Prado.WebUI.TDatePicker,
var day = Prado.WebUI.TDatePicker.getDayListControl(this.control);
var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control);
var year = Prado.WebUI.TDatePicker.getYearListControl(this.control);
- Event.observe (day, "change", this.onDateChanged.bindEvent(this));
- Event.observe (month, "change", this.onDateChanged.bindEvent(this));
- Event.observe (year, "change", this.onDateChanged.bindEvent(this));
+ if (day) Event.observe (day, "change", this.onDateChanged.bindEvent(this));
+ if (month) Event.observe (month, "change", this.onDateChanged.bindEvent(this));
+ if (year) Event.observe (year, "change", this.onDateChanged.bindEvent(this));
}
@@ -66,9 +66,12 @@ Prado.WebUI.TActiveDatePicker = Class.extend(Prado.WebUI.TDatePicker,
}
else
{
- var day = Prado.WebUI.TDatePicker.getDayListControl(this.control).selectedIndex+1;
- var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control).selectedIndex;
- var year = Prado.WebUI.TDatePicker.getYearListControl(this.control).value;
+ var day = Prado.WebUI.TDatePicker.getDayListControl(this.control);
+ if (day) day=day.selectedIndex+1;
+ var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control);
+ if (month) month=month.selectedIndex;
+ var year = Prado.WebUI.TDatePicker.getYearListControl(this.control);
+ if (year) year=year.value;
date=new Date(year, month, day, 0,0,0).SimpleFormat(this.Format, this);
}
if (typeof(this.options.OnDateChanged) == "function") this.options.OnDateChanged(this, date);
diff --git a/framework/Web/Javascripts/source/prado/activefileupload/activefileupload.js b/framework/Web/Javascripts/source/prado/activefileupload/activefileupload.js
index f42a0673..de633c77 100755
--- a/framework/Web/Javascripts/source/prado/activefileupload/activefileupload.js
+++ b/framework/Web/Javascripts/source/prado/activefileupload/activefileupload.js
@@ -46,20 +46,41 @@ Prado.WebUI.TActiveFileUpload = Base.extend(
},
finishUpload : function(options){
+
+ if (this.options.targetID == options.targetID)
+ {
+ this.finishoptions = options;
+ var e = this;
+ var callback =
+ {
+ 'CallbackParameter' : options || '',
+ 'onSuccess' : function() { e.finishCallBack(true); },
+ 'onFailure' : function() { e.finishCallBack(false); }
+ };
+
+ Object.extend(callback, this.options);
+
+ request = new Prado.CallbackRequest(this.options.EventTarget, callback);
+ request.dispatch();
+ }
+ else
+ this.finishCallBack(true);
+
+ },
+
+ finishCallBack : function(success){
// hide the display indicator.
this.flag.value = '';
this.indicator.style.display = 'none';
- if (this.options.targetID == options.targetID){
- // show the complete indicator.
- if (options.errorCode == 0){
- this.complete.style.display = '';
- this.input.value = '';
- } else {
- this.error.style.display = '';
- }
- Prado.Callback(this.options.EventTarget, options, null, this.options);
- }
+ // show the complete indicator.
+ if ((this.finishoptions.errorCode == 0) && (success)) {
+ this.complete.style.display = '';
+ this.input.value = '';
+ } else {
+ this.error.style.display = '';
+ }
}
+
},
{
// class methods
diff --git a/framework/Web/Javascripts/source/prado/validator/validation3.js b/framework/Web/Javascripts/source/prado/validator/validation3.js
index ed9493be..b1bf1a03 100644
--- a/framework/Web/Javascripts/source/prado/validator/validation3.js
+++ b/framework/Web/Javascripts/source/prado/validator/validation3.js
@@ -795,7 +795,7 @@ Prado.WebUI.TBaseValidator.prototype =
* Wether the validator is enabled (default true)
* @var {boolean} enabled
*/
- this.enabled = true;
+ this.enabled = options.Enabled;
/**
* Visibility state of validator(default false)
* @var {boolean} visible
@@ -837,6 +837,7 @@ Prado.WebUI.TBaseValidator.prototype =
* @var {element} message
*/
this.message = $(options.ID);
+
Prado.Registry.set(options.ID, this);
if(this.control && this.message)
{
@@ -1706,7 +1707,7 @@ Prado.WebUI.TRegularExpressionValidator = Class.extend(Prado.WebUI.TBaseValidato
if (value.length <= 0)
return true;
- var rx = new RegExp(this.options.ValidationExpression,this.options.PatternModifiers);
+ var rx = new RegExp('^'+this.options.ValidationExpression+'$',this.options.PatternModifiers);
var matches = rx.exec(value);
return (matches != null && value == matches[0]);
}
diff --git a/framework/Web/THttpRequest.php b/framework/Web/THttpRequest.php
index 7100a4c5..c3926d08 100644
--- a/framework/Web/THttpRequest.php
+++ b/framework/Web/THttpRequest.php
@@ -105,7 +105,7 @@ class THttpRequest extends TApplicationComponent implements IteratorAggregate,Ar
/**
* @var boolean whether the session ID should be kept in cookie only
*/
- private $_cookieOnly=false;
+ private $_cookieOnly=null;
private $_urlFormat=THttpRequestUrlFormat::Get;
private $_services;
private $_requestResolved=false;
@@ -172,8 +172,6 @@ class THttpRequest extends TApplicationComponent implements IteratorAggregate,Ar
$_SERVER['HTTP_USER_AGENT']='';
}
- $this->_cookieOnly=(int)ini_get('session.use_cookies') && (int)ini_get('session.use_only_cookies');
-
// Info about server variables:
// PHP_SELF contains real URI (w/ path info, w/o query string)
// SCRIPT_NAME is the real URI for the requested script (w/o path info and query string)
@@ -568,6 +566,8 @@ class THttpRequest extends TApplicationComponent implements IteratorAggregate,Ar
*/
public function constructUrl($serviceID,$serviceParam,$getItems=null,$encodeAmpersand=true,$encodeGetItems=true)
{
+ if ($this->_cookieOnly===null)
+ $this->_cookieOnly=(int)ini_get('session.use_cookies') && (int)ini_get('session.use_only_cookies');
$url=$this->_urlManager->constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems);
if(defined('SID') && SID != '' && !$this->_cookieOnly)
return $url . (strpos($url,'?')===false? '?' : ($encodeAmpersand?'&amp;':'&')) . SID;
diff --git a/framework/Web/THttpResponse.php b/framework/Web/THttpResponse.php
index 3222f22f..c15f2836 100644
--- a/framework/Web/THttpResponse.php
+++ b/framework/Web/THttpResponse.php
@@ -66,6 +66,9 @@ Prado::using('System.Web.THttpResponseAdapter');
*/
class THttpResponse extends TModule implements ITextWriter
{
+ const DEFAULT_CONTENTTYPE = 'text/html';
+ const DEFAULT_CHARSET = 'UTF-8';
+
/**
* @var The differents defined status code by RFC 2616 {@link http://www.faqs.org/rfcs/rfc2616}
*/
@@ -106,7 +109,7 @@ class THttpResponse extends TModule implements ITextWriter
*/
private $_contentType=null;
/**
- * @var string character set, e.g. UTF-8
+ * @var string|boolean character set, e.g. UTF-8 or false if no character set should be send to client
*/
private $_charset='';
/**
@@ -212,7 +215,7 @@ class THttpResponse extends TModule implements ITextWriter
}
/**
- * @return string output charset.
+ * @return string|boolean output charset.
*/
public function getCharset()
{
@@ -220,11 +223,11 @@ class THttpResponse extends TModule implements ITextWriter
}
/**
- * @param string output charset.
+ * @param string|boolean output charset.
*/
public function setCharset($charset)
{
- $this->_charset = $charset;
+ $this->_charset = (strToLower($charset) === 'false') ? false : (string)$charset;
}
/**
@@ -465,20 +468,22 @@ class THttpResponse extends TModule implements ITextWriter
}
/**
- * Sends content type header if charset is not empty.
+ * Sends content type header with optional charset.
*/
protected function sendContentTypeHeader()
{
+ $contentType=$this->_contentType===null?self::DEFAULT_CONTENTTYPE:$this->_contentType;
$charset=$this->getCharset();
+ if($charset === false) {
+ $this->appendHeader('Content-Type: '.$contentType);
+ return;
+ }
+
if($charset==='' && ($globalization=$this->getApplication()->getGlobalization(false))!==null)
$charset=$globalization->getCharset();
- if($charset!=='')
- {
- $contentType=$this->_contentType===null?'text/html':$this->_contentType;
- $this->appendHeader('Content-Type: '.$contentType.';charset='.$charset);
- }
- else if($this->_contentType!==null)
- $this->appendHeader('Content-Type: '.$this->_contentType.';charset=UTF-8');
+
+ if($charset==='') $charset = self::DEFAULT_CHARSET;
+ $this->appendHeader('Content-Type: '.$contentType.';charset='.$charset);
}
/**
diff --git a/framework/Web/THttpSession.php b/framework/Web/THttpSession.php
index 96d70704..e9f815e5 100644
--- a/framework/Web/THttpSession.php
+++ b/framework/Web/THttpSession.php
@@ -4,7 +4,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2008 PradoSoft
+ * @copyright Copyright &copy; 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web
@@ -115,7 +115,7 @@ class THttpSession extends TApplicationComponent implements IteratorAggregate,Ar
if($this->_autoStart)
$this->open();
$this->_initialized=true;
- $this->getApplication()->setSession($this);
+ $this->getApplication()->setSession($this);
register_shutdown_function(array($this, "close"));
}
@@ -296,6 +296,7 @@ class THttpSession extends TApplicationComponent implements IteratorAggregate,Ar
{
ini_set('session.use_cookies','1');
ini_set('session.use_only_cookies','1');
+ ini_set('session.use_trans_sid', 0);
}
}
}
@@ -366,7 +367,12 @@ class THttpSession extends TApplicationComponent implements IteratorAggregate,Ar
if($this->_started)
throw new TInvalidOperationException('httpsession_transid_unchangeable');
else
- ini_set('session.use_trans_sid',TPropertyValue::ensureBoolean($value)?'1':'0');
+ {
+ $value=TPropertyValue::ensureBoolean($value);
+ if ($value && $this->getCookieMode()==THttpSessionCookieMode::Only)
+ throw new TInvalidOperationException('httpsession_transid_cookieonly');
+ ini_set('session.use_trans_sid',$value?'1':'0');
+ }
}
/**
diff --git a/framework/Web/UI/ActiveControls/TDropContainer.php b/framework/Web/UI/ActiveControls/TDropContainer.php
index 8f7792f3..9c2e1dd5 100755
--- a/framework/Web/UI/ActiveControls/TDropContainer.php
+++ b/framework/Web/UI/ActiveControls/TDropContainer.php
@@ -62,6 +62,13 @@ class TDropContainer extends TPanel implements IActiveControl, ICallbackEventHan
return $this->getAdapter()->getBaseActiveControl();
}
+ /**
+ * @return TCallbackClientSide client side request options.
+ */
+ public function getClientSide()
+ {
+ return $this->getAdapter()->getBaseActiveControl()->getClientSide();
+ }
/**
* Gets the Css class name that this container can accept.
diff --git a/framework/Web/UI/WebControls/TBaseValidator.php b/framework/Web/UI/WebControls/TBaseValidator.php
index 6daae4d0..0c71f46d 100644
--- a/framework/Web/UI/WebControls/TBaseValidator.php
+++ b/framework/Web/UI/WebControls/TBaseValidator.php
@@ -167,6 +167,7 @@ abstract class TBaseValidator extends TLabel implements IValidator
$options['ControlCssClass'] = $this->getControlCssClass();
$options['ControlType'] = $this->getClientControlClass($control);
+ $options['Enabled'] = $this->getEnabled(true);
//get date format from date picker target control
if($control instanceof TDatePicker)
@@ -241,7 +242,7 @@ abstract class TBaseValidator extends TLabel implements IValidator
$scripts->registerPradoScript('validator');
$scripts->registerEndScript($scriptKey, "new Prado.ValidationManager({$options});");
}
- if($this->getEnableClientScript() & $this->getEnabled(true))
+ if($this->getEnableClientScript())
$this->registerClientScriptValidator();
}
diff --git a/framework/Web/UI/WebControls/TBoundColumn.php b/framework/Web/UI/WebControls/TBoundColumn.php
index 0f48d6e7..fdcde9b2 100644
--- a/framework/Web/UI/WebControls/TBoundColumn.php
+++ b/framework/Web/UI/WebControls/TBoundColumn.php
@@ -4,7 +4,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2008 PradoSoft
+ * @copyright Copyright &copy; 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web.UI.WebControls
diff --git a/framework/Web/UI/WebControls/TCheckBoxList.php b/framework/Web/UI/WebControls/TCheckBoxList.php
index 742dd18b..c7c9fc98 100644
--- a/framework/Web/UI/WebControls/TCheckBoxList.php
+++ b/framework/Web/UI/WebControls/TCheckBoxList.php
@@ -4,7 +4,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2008 PradoSoft
+ * @copyright Copyright &copy; 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web.UI.WebControls
@@ -53,7 +53,7 @@ class TCheckBoxList extends TListControl implements IRepeatInfoUser, INamingCont
private $_isEnabled;
private $_changedEventRaised=false;
private $_dataChanged=false;
- private $_isValid=true;
+ private $_isValid=true;
/**
* Constructor.
@@ -254,6 +254,31 @@ class TCheckBoxList extends TListControl implements IRepeatInfoUser, INamingCont
{
return false;
}
+
+ /**
+ * @param boolean whether the control is to be enabled.
+ */
+ public function setEnabled($value)
+ {
+ parent::setEnabled($value);
+ $value = !TPropertyValue::ensureBoolean($value);
+ // if this is an active control,
+ // and it's a callback,
+ // and we can update clientside,
+ // then update the 'disabled' attribute of the items.
+ if(($this instanceof IActiveControl) &&
+ $this->getPage()->getIsCallBack() &&
+ $this->getActiveControl()->canUpdateClientSide())
+ {
+ $items = $this->getItems();
+ $cs = $this->getPage()->getCallbackClient();
+ $baseClientID = $this->getClientID().'_c';
+ foreach($items as $index=>$item)
+ {
+ $cs->setAttribute($baseClientID.$index, 'disabled', $value);
+ }
+ }
+ }
/**
* Returns a style used for rendering items.
@@ -359,16 +384,16 @@ class TCheckBoxList extends TListControl implements IRepeatInfoUser, INamingCont
$page->registerRequiresPostData($this->_repeatedControl);
}
}
-
- /**
- * Wether the list should be rendered inside a span or not
- *
- *@return boolean true if we need a span
- */
- protected function getSpanNeeded ()
- {
- return $this->getRepeatLayout()===TRepeatLayout::Raw;
- }
+
+ /**
+ * Wether the list should be rendered inside a span or not
+ *
+ *@return boolean true if we need a span
+ */
+ protected function getSpanNeeded ()
+ {
+ return $this->getRepeatLayout()===TRepeatLayout::Raw;
+ }
/**
* Renders the checkbox list control.
@@ -379,11 +404,11 @@ class TCheckBoxList extends TListControl implements IRepeatInfoUser, INamingCont
{
if($this->getItemCount()>0)
{
- if ($needSpan=$this->getSpanNeeded())
- {
- $writer->addAttribute('id', $this->getClientId());
- $writer->renderBeginTag('span');
- }
+ if ($needSpan=$this->getSpanNeeded())
+ {
+ $writer->addAttribute('id', $this->getClientId());
+ $writer->renderBeginTag('span');
+ }
$this->_isEnabled=$this->getEnabled(true);
$repeatInfo=$this->getRepeatInfo();
$accessKey=$this->getAccessKey();
@@ -395,8 +420,8 @@ class TCheckBoxList extends TListControl implements IRepeatInfoUser, INamingCont
$this->setTabIndex(0);
$repeatInfo->renderRepeater($writer,$this);
$this->setAccessKey($accessKey);
- $this->setTabIndex($tabIndex);
- if ($needSpan)
+ $this->setTabIndex($tabIndex);
+ if ($needSpan)
$writer->renderEndTag();
}
//checkbox skipped the client control script in addAttributesToRender
@@ -429,23 +454,23 @@ class TCheckBoxList extends TListControl implements IRepeatInfoUser, INamingCont
return $this->getSelectedValue();
}
- /**
- * Returns true if this control validated successfully.
- * Defaults to true.
- * @return bool wether this control validated successfully.
- */
- public function getIsValid()
- {
- return $this->_isValid;
- }
- /**
- * @param bool wether this control is valid.
- */
- public function setIsValid($value)
- {
- $this->_isValid=TPropertyValue::ensureBoolean($value);
- }
-
+ /**
+ * Returns true if this control validated successfully.
+ * Defaults to true.
+ * @return bool wether this control validated successfully.
+ */
+ public function getIsValid()
+ {
+ return $this->_isValid;
+ }
+ /**
+ * @param bool wether this control is valid.
+ */
+ public function setIsValid($value)
+ {
+ $this->_isValid=TPropertyValue::ensureBoolean($value);
+ }
+
/**
* Gets the name of the javascript class responsible for performing postback for this control.
* This method overrides the parent implementation.
@@ -468,7 +493,7 @@ class TCheckBoxList extends TListControl implements IRepeatInfoUser, INamingCont
$options['ListName'] = $this->getUniqueID();
$options['ItemCount'] = $this->getItemCount();
return $options;
- }
+ }
}
diff --git a/framework/Web/UI/WebControls/TDataGridColumn.php b/framework/Web/UI/WebControls/TDataGridColumn.php
index 577c0068..ed53dc88 100644
--- a/framework/Web/UI/WebControls/TDataGridColumn.php
+++ b/framework/Web/UI/WebControls/TDataGridColumn.php
@@ -4,7 +4,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2008 PradoSoft
+ * @copyright Copyright &copy; 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web.UI.WebControls
@@ -418,6 +418,7 @@ abstract class TDataGridColumn extends TApplicationComponent
if(($classPath=$this->getHeaderRenderer())!=='')
{
$control=Prado::createComponent($classPath);
+ $cell->getControls()->add($control);
if($control instanceof IDataRenderer)
{
if($control instanceof IItemDataRenderer)
@@ -428,7 +429,6 @@ abstract class TDataGridColumn extends TApplicationComponent
}
$control->setData($text);
}
- $cell->getControls()->add($control);
}
else if($this->getAllowSorting())
{
@@ -489,6 +489,7 @@ abstract class TDataGridColumn extends TApplicationComponent
if(($classPath=$this->getFooterRenderer())!=='')
{
$control=Prado::createComponent($classPath);
+ $cell->getControls()->add($control);
if($control instanceof IDataRenderer)
{
if($control instanceof IItemDataRenderer)
@@ -499,7 +500,6 @@ abstract class TDataGridColumn extends TApplicationComponent
}
$control->setData($text);
}
- $cell->getControls()->add($control);
}
else if($text!=='')
$cell->setText($text);
diff --git a/framework/Web/UI/WebControls/TJavascriptLogger.php b/framework/Web/UI/WebControls/TJavascriptLogger.php
index 6d49f801..778b6728 100644
--- a/framework/Web/UI/WebControls/TJavascriptLogger.php
+++ b/framework/Web/UI/WebControls/TJavascriptLogger.php
@@ -4,7 +4,7 @@
*
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2008 PradoSoft
+ * @copyright Copyright &copy; 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web.UI.WebControls
@@ -23,7 +23,7 @@
*
* To see the logger and console, press ALT-D (or CTRL-D on OS X).
* More information on the logger can be found at
- * http://gleepglop.com/javascripts/logger/
+ * http://web.archive.org/web/20060512041505/gleepglop.com/javascripts/logger/
*
* @author Wei Zhuo<weizhuo[at]gmail[dot]com>
* @version $Id$
@@ -84,7 +84,7 @@ class TJavascriptLogger extends TWebControl
public function renderContents($writer)
{
$code = strtoupper($this->getToggleKey());
- $info = '(<a href="http://gleepglop.com/javascripts/logger/" target="_blank">more info</a>).';
+ $info = '(<a href="http://web.archive.org/web/20060512041505/gleepglop.com/javascripts/logger/" target="_blank">more info</a>).';
$link = '<a href="javascript:if(logConsole)logConsole.toggle()">toggle the javascript log console.</a>';
$usage = 'Press ALT-'.$code.' (Or CTRL-'.$code.' on OS X) to';
$writer->write("{$usage} {$link} {$info}");
diff --git a/framework/Web/UI/WebControls/TRegularExpressionValidator.php b/framework/Web/UI/WebControls/TRegularExpressionValidator.php
index be861e45..6855c6de 100644
--- a/framework/Web/UI/WebControls/TRegularExpressionValidator.php
+++ b/framework/Web/UI/WebControls/TRegularExpressionValidator.php
@@ -4,7 +4,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2008 PradoSoft
+ * @copyright Copyright &copy; 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web.UI.WebControls
@@ -86,7 +86,7 @@ class TRegularExpressionValidator extends TBaseValidator
{
if(($value=$this->getValidationValue($this->getValidationTarget()))==='')
return true;
- if(($expression=$this->getRegularExpression())!=='')
+ if(($expression=addcslashes($this->getRegularExpression(),"/"))!=='')
{
$mods = $this->getPatternModifiers();
return preg_match("/^$expression\$/{$mods}",$value);
diff --git a/framework/Web/UI/WebControls/TTabPanel.php b/framework/Web/UI/WebControls/TTabPanel.php
index 961d0797..5deced79 100644
--- a/framework/Web/UI/WebControls/TTabPanel.php
+++ b/framework/Web/UI/WebControls/TTabPanel.php
@@ -413,6 +413,7 @@ class TTabPanel extends TWebControl implements IPostBackDataHandler
$cs->registerEndScript("prado:$id", $code);
$cs->registerHiddenField($id.'_1',$this->getActiveViewIndex());
$page->registerRequiresPostData($this);
+ $page->registerRequiresPostData($id."_1");
}
/**
diff --git a/framework/Web/UI/WebControls/TTextBox.php b/framework/Web/UI/WebControls/TTextBox.php
index 4e6a66e9..8ab548a3 100644
--- a/framework/Web/UI/WebControls/TTextBox.php
+++ b/framework/Web/UI/WebControls/TTextBox.php
@@ -245,7 +245,7 @@ class TTextBox extends TWebControl implements IPostBackDataHandler, IValidatable
}
/**
- * Returns true if this control validated successfully.
+ * Returns true if this control validated successfully.
* Defaults to true.
* @return bool wether this control validated successfully.
*/
@@ -299,6 +299,18 @@ class TTextBox extends TWebControl implements IPostBackDataHandler, IValidatable
}
/**
+ * Renders an additional line-break after the opening tag when it
+ * is in MultiLine text mode.
+ * @param THtmlWriter the writer used for the rendering purpose^M
+ */
+ public function renderBeginTag($writer)
+ {
+ parent::renderBeginTag($writer);
+ if($this->getTextMode()==='MultiLine')
+ $writer->write("\n");
+ }
+
+ /**
* @return TTextBoxAutoCompleteType the AutoComplete type of the textbox
*/
public function getAutoCompleteType()
diff --git a/framework/Web/UI/WebControls/TValidationSummary.php b/framework/Web/UI/WebControls/TValidationSummary.php
index ab066f78..4ed2eebe 100644
--- a/framework/Web/UI/WebControls/TValidationSummary.php
+++ b/framework/Web/UI/WebControls/TValidationSummary.php
@@ -149,6 +149,22 @@ class TValidationSummary extends TWebControl
}
/**
+ * @return boolean whether scroll summary into viewport or not. Defaults to true.
+ */
+ public function getScrollToSummary()
+ {
+ return $this->getViewState('ScrollToSummary',true);
+ }
+
+ /**
+ * @param boolean whether scroll summary into viewport or not.
+ */
+ public function setScrollToSummary($value)
+ {
+ $this->setViewState('ScrollToSummary',TPropertyValue::ensureBoolean($value),true);
+ }
+
+ /**
* @return boolean whether the validation summary should be anchored. Defaults to false.
*/
public function getShowAnchor()
@@ -254,6 +270,7 @@ class TValidationSummary extends TWebControl
if(!$this->getShowSummary())
$options['ShowSummary']=false;
+ $options['ScrollToSummary']=$this->getScrollToSummary();
$options['HeaderText']=$this->getHeaderText();
$options['DisplayMode']=$this->getDisplayMode();
diff --git a/framework/pradolite.php b/framework/pradolite.php
new file mode 100644
index 00000000..6d5b13fd
--- /dev/null
+++ b/framework/pradolite.php
@@ -0,0 +1,9489 @@
+<?php
+/**
+ * File Name: pradolite.php
+ * Last Update: 2009/05/29 18:09:00
+ * Generated By: buildscripts/phpbuilder/build.php
+ *
+ * This file is used in lieu of prado.php to boost PRADO application performance.
+ * It is generated by expanding prado.php with included files.
+ * Comments and trace statements are stripped off.
+ *
+ * Do not modify this file manually.
+ */
+
+if(!defined('PRADO_DIR'))
+ define('PRADO_DIR',dirname(__FILE__));
+if(!defined('PRADO_CHMOD'))
+ define('PRADO_CHMOD',0777);
+class PradoBase
+{
+ const CLASS_FILE_EXT='.php';
+ private static $_aliases=array('System'=>PRADO_DIR);
+ private static $_usings=array();
+ private static $_application=null;
+ private static $_logger=null;
+ public static function getVersion()
+ {
+ return '3.1.6-dev';
+ }
+ public static function initErrorHandlers()
+ {
+ set_error_handler(array('PradoBase','phpErrorHandler'),error_reporting());
+ set_exception_handler(array('PradoBase','exceptionHandler'));
+ }
+ public static function autoload($className)
+ {
+ include_once($className.self::CLASS_FILE_EXT);
+ if(!class_exists($className,false) && !interface_exists($className,false))
+ self::fatalError("Class file for '$className' cannot be found.");
+ }
+ public static function poweredByPrado($logoType=0)
+ {
+ $logoName=$logoType==1?'powered2':'powered';
+ if(self::$_application!==null)
+ {
+ $am=self::$_application->getAssetManager();
+ $url=$am->publishFilePath(self::getPathOfNamespace('System.'.$logoName,'.gif'));
+ }
+ else
+ $url='http://www.pradosoft.com/images/'.$logoName.'.gif';
+ return '<a title="Powered by PRADO" href="http://www.pradosoft.com/" target="_blank"><img src="'.$url.'" style="border-width:0px;" alt="Powered by PRADO" /></a>';
+ }
+ public static function phpErrorHandler($errno,$errstr,$errfile,$errline)
+ {
+ if(error_reporting()!=0)
+ throw new TPhpErrorException($errno,$errstr,$errfile,$errline);
+ }
+ public static function exceptionHandler($exception)
+ {
+ if(self::$_application!==null && ($errorHandler=self::$_application->getErrorHandler())!==null)
+ {
+ $errorHandler->handleError(null,$exception);
+ }
+ else
+ {
+ echo $exception;
+ }
+ exit(1);
+ }
+ public static function setApplication($application)
+ {
+ if(self::$_application!==null)
+ throw new TInvalidOperationException('prado_application_singleton_required');
+ self::$_application=$application;
+ }
+ public static function getApplication()
+ {
+ return self::$_application;
+ }
+ public static function getFrameworkPath()
+ {
+ return PRADO_DIR;
+ }
+ public static function serialize($data)
+ {
+ $arr[0]=$data;
+ return serialize($arr);
+ }
+ public static function unserialize($str)
+ {
+ $arr=unserialize($str);
+ return isset($arr[0])?$arr[0]:null;
+ }
+ public static function createComponent($type)
+ {
+ self::using($type);
+ if(($pos=strrpos($type,'.'))!==false)
+ $type=substr($type,$pos+1);
+ if(($n=func_num_args())>1)
+ {
+ $args=func_get_args();
+ $s='$args[1]';
+ for($i=2;$i<$n;++$i)
+ $s.=",\$args[$i]";
+ eval("\$component=new $type($s);");
+ return $component;
+ }
+ else
+ return new $type;
+ }
+ public static function using($namespace,$checkClassExistence=true)
+ {
+ if(isset(self::$_usings[$namespace]) || class_exists($namespace,false))
+ return;
+ if(($pos=strrpos($namespace,'.'))===false) {
+ try
+ {
+ include_once($namespace.self::CLASS_FILE_EXT);
+ }
+ catch(Exception $e)
+ {
+ if($checkClassExistence && !class_exists($namespace,false))
+ throw new TInvalidOperationException('prado_component_unknown',$namespace,$e->getMessage());
+ else
+ throw $e;
+ }
+ }
+ else if(($path=self::getPathOfNamespace($namespace,self::CLASS_FILE_EXT))!==null)
+ {
+ $className=substr($namespace,$pos+1);
+ if($className==='*') {
+ self::$_usings[$namespace]=$path;
+ set_include_path(get_include_path().PATH_SEPARATOR.$path);
+ }
+ else {
+ self::$_usings[$namespace]=$path;
+ if(!$checkClassExistence || !class_exists($className,false))
+ {
+ try
+ {
+ include_once($path);
+ }
+ catch(Exception $e)
+ {
+ if($checkClassExistence && !class_exists($className,false))
+ throw new TInvalidOperationException('prado_component_unknown',$className,$e->getMessage());
+ else
+ throw $e;
+ }
+ }
+ }
+ }
+ else
+ throw new TInvalidDataValueException('prado_using_invalid',$namespace);
+ }
+ public static function getPathOfNamespace($namespace,$ext='')
+ {
+ if(isset(self::$_usings[$namespace]))
+ return self::$_usings[$namespace];
+ else if(isset(self::$_aliases[$namespace]))
+ return self::$_aliases[$namespace];
+ else
+ {
+ $segs=explode('.',$namespace);
+ $alias=array_shift($segs);
+ if(($file=array_pop($segs))!==null && ($root=self::getPathOfAlias($alias))!==null)
+ return rtrim($root.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR ,$segs),'/\\').(($file==='*')?'':DIRECTORY_SEPARATOR.$file.$ext);
+ else
+ return null;
+ }
+ }
+ public static function getPathOfAlias($alias)
+ {
+ return isset(self::$_aliases[$alias])?self::$_aliases[$alias]:null;
+ }
+ protected static function getPathAliases()
+ {
+ return self::$_aliases;
+ }
+ public static function setPathOfAlias($alias,$path)
+ {
+ if(isset(self::$_aliases[$alias]))
+ throw new TInvalidOperationException('prado_alias_redefined',$alias);
+ else if(($rp=realpath($path))!==false && is_dir($rp))
+ {
+ if(strpos($alias,'.')===false)
+ self::$_aliases[$alias]=$rp;
+ else
+ throw new TInvalidDataValueException('prado_aliasname_invalid',$alias);
+ }
+ else
+ throw new TInvalidDataValueException('prado_alias_invalid',$alias,$path);
+ }
+ public static function fatalError($msg)
+ {
+ echo '<h1>Fatal Error</h1>';
+ echo '<p>'.$msg.'</p>';
+ if(!function_exists('debug_backtrace'))
+ return;
+ echo '<h2>Debug Backtrace</h2>';
+ echo '<pre>';
+ $index=-1;
+ foreach(debug_backtrace() as $t)
+ {
+ $index++;
+ if($index==0) continue;
+ echo '#'.$index.' ';
+ if(isset($t['file']))
+ echo basename($t['file']) . ':' . $t['line'];
+ else
+ echo '<PHP inner-code>';
+ echo ' -- ';
+ if(isset($t['class']))
+ echo $t['class'] . $t['type'];
+ echo $t['function'] . '(';
+ if(isset($t['args']) && sizeof($t['args']) > 0)
+ {
+ $count=0;
+ foreach($t['args'] as $item)
+ {
+ if(is_string($item))
+ {
+ $str=htmlentities(str_replace("\r\n", "", $item), ENT_QUOTES);
+ if (strlen($item) > 70)
+ echo "'". substr($str, 0, 70) . "...'";
+ else
+ echo "'" . $str . "'";
+ }
+ else if (is_int($item) || is_float($item))
+ echo $item;
+ else if (is_object($item))
+ echo get_class($item);
+ else if (is_array($item))
+ echo 'array(' . count($item) . ')';
+ else if (is_bool($item))
+ echo $item ? 'true' : 'false';
+ else if ($item === null)
+ echo 'NULL';
+ else if (is_resource($item))
+ echo get_resource_type($item);
+ $count++;
+ if (count($t['args']) > $count)
+ echo ', ';
+ }
+ }
+ echo ")\n";
+ }
+ echo '</pre>';
+ exit(1);
+ }
+ public static function getUserLanguages()
+ {
+ static $languages=null;
+ if($languages===null)
+ {
+ if(!isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
+ $languages[0]='en';
+ else
+ {
+ $languages=array();
+ foreach(explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE']) as $language)
+ {
+ $array=explode(';q=',trim($language));
+ $languages[trim($array[0])]=isset($array[1])?(float)$array[1]:1.0;
+ }
+ arsort($languages);
+ $languages=array_keys($languages);
+ if(empty($languages))
+ $languages[0]='en';
+ }
+ }
+ return $languages;
+ }
+ public static function getPreferredLanguage()
+ {
+ static $language=null;
+ if($language===null)
+ {
+ $langs=Prado::getUserLanguages();
+ $lang=explode('-',$langs[0]);
+ if(empty($lang[0]) || !ctype_alpha($lang[0]))
+ $language='en';
+ else
+ $language=$lang[0];
+ }
+ return $language;
+ }
+ public static function trace($msg,$category='Uncategorized')
+ {
+ if(self::$_application && self::$_application->getMode()===TApplicationMode::Performance)
+ return;
+ if(!self::$_application || self::$_application->getMode()===TApplicationMode::Debug)
+ {
+ $trace=debug_backtrace();
+ if(isset($trace[0]['file']) && isset($trace[0]['line']))
+ $msg.=" (line {$trace[0]['line']}, {$trace[0]['file']})";
+ $level=TLogger::DEBUG;
+ }
+ else
+ $level=TLogger::INFO;
+ self::log($msg,$level,$category);
+ }
+ public static function log($msg,$level=TLogger::INFO,$category='Uncategorized')
+ {
+ if(self::$_logger===null)
+ self::$_logger=new TLogger;
+ self::$_logger->log($msg,$level,$category);
+ }
+ public static function getLogger()
+ {
+ if(self::$_logger===null)
+ self::$_logger=new TLogger;
+ return self::$_logger;
+ }
+ public static function varDump($var,$depth=10,$highlight=false)
+ {
+ Prado::using('System.Util.TVarDumper');
+ return TVarDumper::dump($var,$depth,$highlight);
+ }
+ public static function localize($text, $parameters=array(), $catalogue=null, $charset=null)
+ {
+ Prado::using('System.I18N.Translation');
+ $app = Prado::getApplication()->getGlobalization(false);
+ $params = array();
+ foreach($parameters as $key => $value)
+ $params['{'.$key.'}'] = $value;
+ if($app===null || ($config = $app->getTranslationConfiguration())===null)
+ return strtr($text, $params);
+ if ($catalogue===null)
+ $catalogue=isset($config['catalogue'])?$config['catalogue']:'messages';
+ Translation::init($catalogue);
+ $appCharset = $app===null ? '' : $app->getCharset();
+ $defaultCharset = ($app===null) ? 'UTF-8' : $app->getDefaultCharset();
+ if(empty($charset)) $charset = $appCharset;
+ if(empty($charset)) $charset = $defaultCharset;
+ return Translation::formatter($catalogue)->format($text,$params,$catalogue,$charset);
+ }
+}
+class TReflectionClass extends ReflectionClass
+{
+}
+PradoBase::using('System.TComponent');
+PradoBase::using('System.Exceptions.TException');
+PradoBase::using('System.Util.TLogger');
+if(!class_exists('Prado',false))
+{
+ class Prado extends PradoBase
+ {
+ }
+}
+spl_autoload_register(array('Prado','autoload'));
+Prado::initErrorHandlers();
+interface IModule
+{
+ public function init($config);
+ public function getID();
+ public function setID($id);
+}
+interface IService
+{
+ public function init($config);
+ public function getID();
+ public function setID($id);
+ public function getEnabled();
+ public function setEnabled($value);
+ public function run();
+}
+interface ITextWriter
+{
+ public function write($str);
+ public function flush();
+}
+interface IUser
+{
+ public function getName();
+ public function setName($value);
+ public function getIsGuest();
+ public function setIsGuest($value);
+ public function getRoles();
+ public function setRoles($value);
+ public function isInRole($role);
+ public function saveToString();
+ public function loadFromString($string);
+}
+interface IStatePersister
+{
+ public function load();
+ public function save($state);
+}
+interface ICache
+{
+ public function get($id);
+ public function set($id,$value,$expire=0,$dependency=null);
+ public function add($id,$value,$expire=0,$dependency=null);
+ public function delete($id);
+ public function flush();
+}
+interface ICacheDependency
+{
+ public function getHasChanged();
+}
+interface IRenderable
+{
+ public function render($writer);
+}
+interface IBindable
+{
+ public function dataBind();
+}
+interface IStyleable
+{
+ public function getHasStyle();
+ public function getStyle();
+ public function clearStyle();
+}
+interface IActiveControl
+{
+ public function getActiveControl();
+}
+interface ICallbackEventHandler
+{
+ public function raiseCallbackEvent($eventArgument);
+}
+interface IDataRenderer
+{
+ public function getData();
+ public function setData($value);
+}
+class TApplicationComponent extends TComponent
+{
+ public function getApplication()
+ {
+ return Prado::getApplication();
+ }
+ public function getService()
+ {
+ return Prado::getApplication()->getService();
+ }
+ public function getRequest()
+ {
+ return Prado::getApplication()->getRequest();
+ }
+ public function getResponse()
+ {
+ return Prado::getApplication()->getResponse();
+ }
+ public function getSession()
+ {
+ return Prado::getApplication()->getSession();
+ }
+ public function getUser()
+ {
+ return Prado::getApplication()->getUser();
+ }
+ public function publishAsset($assetPath,$className=null)
+ {
+ if($className===null)
+ $className=get_class($this);
+ $class=new ReflectionClass($className);
+ $fullPath=dirname($class->getFileName()).DIRECTORY_SEPARATOR.$assetPath;
+ return $this->publishFilePath($fullPath);
+ }
+ public function publishFilePath($fullPath)
+ {
+ return Prado::getApplication()->getAssetManager()->publishFilePath($fullPath);
+ }
+}
+abstract class TModule extends TApplicationComponent implements IModule
+{
+ private $_id;
+ public function init($config)
+ {
+ }
+ public function getID()
+ {
+ return $this->_id;
+ }
+ public function setID($value)
+ {
+ $this->_id=$value;
+ }
+}
+abstract class TService extends TApplicationComponent implements IService
+{
+ private $_id;
+ private $_enabled=true;
+ public function init($config)
+ {
+ }
+ public function getID()
+ {
+ return $this->_id;
+ }
+ public function setID($value)
+ {
+ $this->_id=$value;
+ }
+ public function getEnabled()
+ {
+ return $this->_enabled;
+ }
+ public function setEnabled($value)
+ {
+ $this->_enabled=TPropertyValue::ensureBoolean($value);
+ }
+ public function run()
+ {
+ }
+}
+class TErrorHandler extends TModule
+{
+ const ERROR_FILE_NAME='error';
+ const EXCEPTION_FILE_NAME='exception';
+ const SOURCE_LINES=12;
+ private $_templatePath=null;
+ public function init($config)
+ {
+ $this->getApplication()->setErrorHandler($this);
+ }
+ public function getErrorTemplatePath()
+ {
+ if($this->_templatePath===null)
+ $this->_templatePath=Prado::getFrameworkPath().'/Exceptions/templates';
+ return $this->_templatePath;
+ }
+ public function setErrorTemplatePath($value)
+ {
+ if(($templatePath=Prado::getPathOfNamespace($value))!==null && is_dir($templatePath))
+ $this->_templatePath=$templatePath;
+ else
+ throw new TConfigurationException('errorhandler_errortemplatepath_invalid',$value);
+ }
+ public function handleError($sender,$param)
+ {
+ static $handling=false;
+ restore_error_handler();
+ restore_exception_handler();
+ if($handling)
+ $this->handleRecursiveError($param);
+ else
+ {
+ $handling=true;
+ if(($response=$this->getResponse())!==null)
+ $response->clear();
+ if(!headers_sent())
+ header('Content-Type: text/html; charset=UTF-8');
+ if($param instanceof THttpException)
+ $this->handleExternalError($param->getStatusCode(),$param);
+ else if($this->getApplication()->getMode()===TApplicationMode::Debug)
+ $this->displayException($param);
+ else
+ $this->handleExternalError(500,$param);
+ }
+ }
+ protected function handleExternalError($statusCode,$exception)
+ {
+ if(!($exception instanceof THttpException))
+ error_log($exception->__toString());
+ $content=$this->getErrorTemplate($statusCode,$exception);
+ $serverAdmin=isset($_SERVER['SERVER_ADMIN'])?$_SERVER['SERVER_ADMIN']:'';
+ if($this->getApplication()->getMode()===TApplicationMode::Debug)
+ $version=$_SERVER['SERVER_SOFTWARE'].' <a href="http://www.pradosoft.com/">PRADO</a>/'.Prado::getVersion();
+ else
+ $version='';
+ $tokens=array(
+ '%%StatusCode%%' => "$statusCode",
+ '%%ErrorMessage%%' => htmlspecialchars($exception->getMessage()),
+ '%%ServerAdmin%%' => $serverAdmin,
+ '%%Version%%' => $version,
+ '%%Time%%' => @strftime('%Y-%m-%d %H:%M',time())
+ );
+ header("HTTP/1.0 $statusCode ".$exception->getMessage());
+ echo strtr($content,$tokens);
+ }
+ protected function handleRecursiveError($exception)
+ {
+ if($this->getApplication()->getMode()===TApplicationMode::Debug)
+ {
+ echo "<html><head><title>Recursive Error</title></head>\n";
+ echo "<body><h1>Recursive Error</h1>\n";
+ echo "<pre>".$exception->__toString()."</pre>\n";
+ echo "</body></html>";
+ }
+ else
+ {
+ error_log("Error happened while processing an existing error:\n".$exception->__toString());
+ header('HTTP/1.0 500 Internal Error');
+ }
+ }
+ protected function displayException($exception)
+ {
+ if(php_sapi_name()==='cli')
+ {
+ echo $exception->getMessage()."\n";
+ echo $exception->getTraceAsString();
+ return;
+ }
+ if($exception instanceof TTemplateException)
+ {
+ $fileName=$exception->getTemplateFile();
+ $lines=empty($fileName)?explode("\n",$exception->getTemplateSource()):@file($fileName);
+ $source=$this->getSourceCode($lines,$exception->getLineNumber());
+ if($fileName==='')
+ $fileName='---embedded template---';
+ $errorLine=$exception->getLineNumber();
+ }
+ else
+ {
+ if(($trace=$this->getExactTrace($exception))!==null)
+ {
+ $fileName=$trace['file'];
+ $errorLine=$trace['line'];
+ }
+ else
+ {
+ $fileName=$exception->getFile();
+ $errorLine=$exception->getLine();
+ }
+ $source=$this->getSourceCode(@file($fileName),$errorLine);
+ }
+ if($this->getApplication()->getMode()===TApplicationMode::Debug)
+ $version=$_SERVER['SERVER_SOFTWARE'].' <a href="http://www.pradosoft.com/">PRADO</a>/'.Prado::getVersion();
+ else
+ $version='';
+ $tokens=array(
+ '%%ErrorType%%' => get_class($exception),
+ '%%ErrorMessage%%' => $this->addLink(htmlspecialchars($exception->getMessage())),
+ '%%SourceFile%%' => htmlspecialchars($fileName).' ('.$errorLine.')',
+ '%%SourceCode%%' => $source,
+ '%%StackTrace%%' => htmlspecialchars($exception->getTraceAsString()),
+ '%%Version%%' => $version,
+ '%%Time%%' => @strftime('%Y-%m-%d %H:%M',time())
+ );
+ $content=$this->getExceptionTemplate($exception);
+ echo strtr($content,$tokens);
+ }
+ protected function getExceptionTemplate($exception)
+ {
+ $lang=Prado::getPreferredLanguage();
+ $exceptionFile=Prado::getFrameworkPath().'/Exceptions/templates/'.self::EXCEPTION_FILE_NAME.'-'.$lang.'.html';
+ if(!is_file($exceptionFile))
+ $exceptionFile=Prado::getFrameworkPath().'/Exceptions/templates/'.self::EXCEPTION_FILE_NAME.'.html';
+ if(($content=@file_get_contents($exceptionFile))===false)
+ die("Unable to open exception template file '$exceptionFile'.");
+ return $content;
+ }
+ protected function getErrorTemplate($statusCode,$exception)
+ {
+ $base=$this->getErrorTemplatePath().DIRECTORY_SEPARATOR.self::ERROR_FILE_NAME;
+ $lang=Prado::getPreferredLanguage();
+ if(is_file("$base$statusCode-$lang.html"))
+ $errorFile="$base$statusCode-$lang.html";
+ else if(is_file("$base$statusCode.html"))
+ $errorFile="$base$statusCode.html";
+ else if(is_file("$base-$lang.html"))
+ $errorFile="$base-$lang.html";
+ else
+ $errorFile="$base.html";
+ if(($content=@file_get_contents($errorFile))===false)
+ die("Unable to open error template file '$errorFile'.");
+ return $content;
+ }
+ private function getExactTrace($exception)
+ {
+ $trace=$exception->getTrace();
+ $result=null;
+ if($exception instanceof TPhpErrorException)
+ $result=isset($trace[0]['file'])?$trace[0]:$trace[1];
+ else if($exception instanceof TInvalidOperationException)
+ {
+ if(($result=$this->getPropertyAccessTrace($trace,'__get'))===null)
+ $result=$this->getPropertyAccessTrace($trace,'__set');
+ }
+ if($result!==null && strpos($result['file'],': eval()\'d code')!==false)
+ return null;
+ return $result;
+ }
+ private function getPropertyAccessTrace($trace,$pattern)
+ {
+ $result=null;
+ foreach($trace as $t)
+ {
+ if(isset($t['function']) && $t['function']===$pattern)
+ $result=$t;
+ else
+ break;
+ }
+ return $result;
+ }
+ private function getSourceCode($lines,$errorLine)
+ {
+ $beginLine=$errorLine-self::SOURCE_LINES>=0?$errorLine-self::SOURCE_LINES:0;
+ $endLine=$errorLine+self::SOURCE_LINES<=count($lines)?$errorLine+self::SOURCE_LINES:count($lines);
+ $source='';
+ for($i=$beginLine;$i<$endLine;++$i)
+ {
+ if($i===$errorLine-1)
+ {
+ $line=htmlspecialchars(sprintf("%04d: %s",$i+1,str_replace("\t",' ',$lines[$i])));
+ $source.="<div class=\"error\">".$line."</div>";
+ }
+ else
+ $source.=htmlspecialchars(sprintf("%04d: %s",$i+1,str_replace("\t",' ',$lines[$i])));
+ }
+ return $source;
+ }
+ private function addLink($message)
+ {
+ $baseUrl='http://www.pradosoft.com/docs/classdoc';
+ return preg_replace('/\b(T[A-Z]\w+)\b/',"<a href=\"$baseUrl/\${1}\" target=\"_blank\">\${1}</a>",$message);
+ }
+}
+class TList extends TComponent implements IteratorAggregate,ArrayAccess,Countable
+{
+ private $_d=array();
+ private $_c=0;
+ private $_r=false;
+ public function __construct($data=null,$readOnly=false)
+ {
+ if($data!==null)
+ $this->copyFrom($data);
+ $this->setReadOnly($readOnly);
+ }
+ public function getReadOnly()
+ {
+ return $this->_r;
+ }
+ protected function setReadOnly($value)
+ {
+ $this->_r=TPropertyValue::ensureBoolean($value);
+ }
+ public function getIterator()
+ {
+ return new TListIterator($this->_d);
+ }
+ public function count()
+ {
+ return $this->getCount();
+ }
+ public function getCount()
+ {
+ return $this->_c;
+ }
+ public function itemAt($index)
+ {
+ if($index>=0 && $index<$this->_c)
+ return $this->_d[$index];
+ else
+ throw new TInvalidDataValueException('list_index_invalid',$index);
+ }
+ public function add($item)
+ {
+ $this->insertAt($this->_c,$item);
+ return $this->_c-1;
+ }
+ public function insertAt($index,$item)
+ {
+ if(!$this->_r)
+ {
+ if($index===$this->_c)
+ $this->_d[$this->_c++]=$item;
+ else if($index>=0 && $index<$this->_c)
+ {
+ array_splice($this->_d,$index,0,array($item));
+ $this->_c++;
+ }
+ else
+ throw new TInvalidDataValueException('list_index_invalid',$index);
+ }
+ else
+ throw new TInvalidOperationException('list_readonly',get_class($this));
+ }
+ public function remove($item)
+ {
+ if(($index=$this->indexOf($item))>=0)
+ {
+ $this->removeAt($index);
+ return $index;
+ }
+ else
+ throw new TInvalidDataValueException('list_item_inexistent');
+ }
+ public function removeAt($index)
+ {
+ if(!$this->_r)
+ {
+ if($index>=0 && $index<$this->_c)
+ {
+ $this->_c--;
+ if($index===$this->_c)
+ return array_pop($this->_d);
+ else
+ {
+ $item=$this->_d[$index];
+ array_splice($this->_d,$index,1);
+ return $item;
+ }
+ }
+ else
+ throw new TInvalidDataValueException('list_index_invalid',$index);
+ }
+ else
+ throw new TInvalidOperationException('list_readonly',get_class($this));
+ }
+ public function clear()
+ {
+ for($i=$this->_c-1;$i>=0;--$i)
+ $this->removeAt($i);
+ }
+ public function contains($item)
+ {
+ return $this->indexOf($item)>=0;
+ }
+ public function indexOf($item)
+ {
+ if(($index=array_search($item,$this->_d,true))===false)
+ return -1;
+ else
+ return $index;
+ }
+ public function toArray()
+ {
+ return $this->_d;
+ }
+ public function copyFrom($data)
+ {
+ if(is_array($data) || ($data instanceof Traversable))
+ {
+ if($this->_c>0)
+ $this->clear();
+ foreach($data as $item)
+ $this->add($item);
+ }
+ else if($data!==null)
+ throw new TInvalidDataTypeException('list_data_not_iterable');
+ }
+ public function mergeWith($data)
+ {
+ if(is_array($data) || ($data instanceof Traversable))
+ {
+ foreach($data as $item)
+ $this->add($item);
+ }
+ else if($data!==null)
+ throw new TInvalidDataTypeException('list_data_not_iterable');
+ }
+ public function offsetExists($offset)
+ {
+ return ($offset>=0 && $offset<$this->_c);
+ }
+ public function offsetGet($offset)
+ {
+ return $this->itemAt($offset);
+ }
+ public function offsetSet($offset,$item)
+ {
+ if($offset===null || $offset===$this->_c)
+ $this->insertAt($this->_c,$item);
+ else
+ {
+ $this->removeAt($offset);
+ $this->insertAt($offset,$item);
+ }
+ }
+ public function offsetUnset($offset)
+ {
+ $this->removeAt($offset);
+ }
+}
+class TListIterator implements Iterator
+{
+ private $_d;
+ private $_i;
+ private $_c;
+ public function __construct(&$data)
+ {
+ $this->_d=&$data;
+ $this->_i=0;
+ $this->_c=count($this->_d);
+ }
+ public function rewind()
+ {
+ $this->_i=0;
+ }
+ public function key()
+ {
+ return $this->_i;
+ }
+ public function current()
+ {
+ return $this->_d[$this->_i];
+ }
+ public function next()
+ {
+ $this->_i++;
+ }
+ public function valid()
+ {
+ return $this->_i<$this->_c;
+ }
+}
+abstract class TCache extends TModule implements ICache, ArrayAccess
+{
+ private $_prefix=null;
+ private $_primary=true;
+ public function init($config)
+ {
+ if($this->_prefix===null)
+ $this->_prefix=$this->getApplication()->getUniqueID();
+ if($this->_primary)
+ {
+ if($this->getApplication()->getCache()===null)
+ $this->getApplication()->setCache($this);
+ else
+ throw new TConfigurationException('cache_primary_duplicated',get_class($this));
+ }
+ }
+ public function getPrimaryCache()
+ {
+ return $this->_primary;
+ }
+ public function setPrimaryCache($value)
+ {
+ $this->_primary=TPropertyValue::ensureBoolean($value);
+ }
+ public function getKeyPrefix()
+ {
+ return $this->_prefix;
+ }
+ public function setKeyPrefix($value)
+ {
+ $this->_prefix=$value;
+ }
+ protected function generateUniqueKey($key)
+ {
+ return md5($this->_prefix.$key);
+ }
+ public function get($id)
+ {
+ if(($value=$this->getValue($this->generateUniqueKey($id)))!==false)
+ {
+ $data=unserialize($value);
+ if(!is_array($data))
+ return false;
+ if(!($data[1] instanceof ICacheDependency) || !$data[1]->getHasChanged())
+ return $data[0];
+ }
+ return false;
+ }
+ public function set($id,$value,$expire=0,$dependency=null)
+ {
+ if(empty($value) && $expire === 0)
+ $this->delete($id);
+ else
+ {
+ $data=array($value,$dependency);
+ return $this->setValue($this->generateUniqueKey($id),serialize($data),$expire);
+ }
+ }
+ public function add($id,$value,$expire=0,$dependency=null)
+ {
+ if(empty($value) && $expire === 0)
+ return false;
+ $data=array($value,$dependency);
+ return $this->addValue($this->generateUniqueKey($id),serialize($data),$expire);
+ }
+ public function delete($id)
+ {
+ return $this->deleteValue($this->generateUniqueKey($id));
+ }
+ public function flush()
+ {
+ throw new TNotSupportedException('cache_flush_unsupported');
+ }
+ abstract protected function getValue($key);
+ abstract protected function setValue($key,$value,$expire);
+ abstract protected function addValue($key,$value,$expire);
+ abstract protected function deleteValue($key);
+ public function offsetExists($id)
+ {
+ return $this->get($id) !== false;
+ }
+ public function offsetGet($id)
+ {
+ return $this->get($id);
+ }
+ public function offsetSet($id, $value)
+ {
+ $this->set($id, $value);
+ }
+ public function offsetUnset($id)
+ {
+ $this->delete($id);
+ }
+}
+abstract class TCacheDependency extends TComponent implements ICacheDependency
+{
+}
+class TFileCacheDependency extends TCacheDependency
+{
+ private $_fileName;
+ private $_timestamp;
+ public function __construct($fileName)
+ {
+ $this->setFileName($fileName);
+ }
+ public function getFileName()
+ {
+ return $this->_fileName;
+ }
+ public function setFileName($value)
+ {
+ $this->_fileName=$value;
+ $this->_timestamp=@filemtime($value);
+ }
+ public function getTimestamp()
+ {
+ return $this->_timestamp;
+ }
+ public function getHasChanged()
+ {
+ return @filemtime($this->_fileName)!==$this->_timestamp;
+ }
+}
+class TDirectoryCacheDependency extends TCacheDependency
+{
+ private $_recursiveCheck=true;
+ private $_recursiveLevel=-1;
+ private $_timestamps;
+ private $_directory;
+ public function __construct($directory)
+ {
+ $this->setDirectory($directory);
+ }
+ public function getDirectory()
+ {
+ return $this->_directory;
+ }
+ public function setDirectory($directory)
+ {
+ if(($path=realpath($directory))===false || !is_dir($path))
+ throw new TInvalidDataValueException('directorycachedependency_directory_invalid',$directory);
+ $this->_directory=$path;
+ $this->_timestamps=$this->generateTimestamps($path);
+ }
+ public function getRecursiveCheck()
+ {
+ return $this->_recursiveCheck;
+ }
+ public function setRecursiveCheck($value)
+ {
+ $this->_recursiveCheck=TPropertyValue::ensureBoolean($value);
+ }
+ public function getRecursiveLevel()
+ {
+ return $this->_recursiveLevel;
+ }
+ public function setRecursiveLevel($value)
+ {
+ $this->_recursiveLevel=TPropertyValue::ensureInteger($value);
+ }
+ public function getHasChanged()
+ {
+ return $this->generateTimestamps($this->_directory)!=$this->_timestamps;
+ }
+ protected function validateFile($fileName)
+ {
+ return true;
+ }
+ protected function validateDirectory($directory)
+ {
+ return true;
+ }
+ protected function generateTimestamps($directory,$level=0)
+ {
+ if(($dir=opendir($directory))===false)
+ throw new TIOException('directorycachedependency_directory_invalid',$directory);
+ $timestamps=array();
+ while(($file=readdir($dir))!==false)
+ {
+ $path=$directory.DIRECTORY_SEPARATOR.$file;
+ if($file==='.' || $file==='..')
+ continue;
+ else if(is_dir($path))
+ {
+ if(($this->_recursiveLevel<0 || $level<$this->_recursiveLevel) && $this->validateDirectory($path))
+ $timestamps=array_merge($this->generateTimestamps($path,$level+1));
+ }
+ else if($this->validateFile($path))
+ $timestamps[$path]=filemtime($path);
+ }
+ closedir($dir);
+ return $timestamps;
+ }
+}
+class TGlobalStateCacheDependency extends TCacheDependency
+{
+ private $_stateName;
+ private $_stateValue;
+ public function __construct($name)
+ {
+ $this->setStateName($name);
+ }
+ public function getStateName()
+ {
+ return $this->_stateName;
+ }
+ public function setStateName($value)
+ {
+ $this->_stateName=$value;
+ $this->_stateValue=Prado::getApplication()->getGlobalState($value);
+ }
+ public function getHasChanged()
+ {
+ return $this->_stateValue!==Prado::getApplication()->getGlobalState($this->_stateName);
+ }
+}
+class TChainedCacheDependency extends TCacheDependency
+{
+ private $_dependencies=null;
+ public function getDependencies()
+ {
+ if($this->_dependencies===null)
+ $this->_dependencies=new TCacheDependencyList;
+ return $this->_dependencies;
+ }
+ public function getHasChanged()
+ {
+ if($this->_dependencies!==null)
+ {
+ foreach($this->_dependencies as $dependency)
+ if($dependency->getHasChanged())
+ return true;
+ }
+ return false;
+ }
+}
+class TApplicationStateCacheDependency extends TCacheDependency
+{
+ public function getHasChanged()
+ {
+ return Prado::getApplication()->getMode()!==TApplicationMode::Performance;
+ }
+}
+class TCacheDependencyList extends TList
+{
+ public function insertAt($index,$item)
+ {
+ if($item instanceof ICacheDependency)
+ parent::insertAt($index,$item);
+ else
+ throw new TInvalidDataTypeException('cachedependencylist_cachedependency_required');
+ }
+}
+class TTextWriter extends TComponent implements ITextWriter
+{
+ private $_str='';
+ public function flush()
+ {
+ $str=$this->_str;
+ $this->_str='';
+ return $str;
+ }
+ public function write($str)
+ {
+ $this->_str.=$str;
+ }
+ public function writeLine($str='')
+ {
+ $this->write($str."\n");
+ }
+}
+class TMap extends TComponent implements IteratorAggregate,ArrayAccess,Countable
+{
+ private $_d=array();
+ private $_r=false;
+ public function __construct($data=null,$readOnly=false)
+ {
+ if($data!==null)
+ $this->copyFrom($data);
+ $this->setReadOnly($readOnly);
+ }
+ public function getReadOnly()
+ {
+ return $this->_r;
+ }
+ protected function setReadOnly($value)
+ {
+ $this->_r=TPropertyValue::ensureBoolean($value);
+ }
+ public function getIterator()
+ {
+ return new TMapIterator($this->_d);
+ }
+ public function count()
+ {
+ return $this->getCount();
+ }
+ public function getCount()
+ {
+ return count($this->_d);
+ }
+ public function getKeys()
+ {
+ return array_keys($this->_d);
+ }
+ public function itemAt($key)
+ {
+ return isset($this->_d[$key]) ? $this->_d[$key] : null;
+ }
+ public function add($key,$value)
+ {
+ if(!$this->_r)
+ $this->_d[$key]=$value;
+ else
+ throw new TInvalidOperationException('map_readonly',get_class($this));
+ }
+ public function remove($key)
+ {
+ if(!$this->_r)
+ {
+ if(isset($this->_d[$key]) || array_key_exists($key,$this->_d))
+ {
+ $value=$this->_d[$key];
+ unset($this->_d[$key]);
+ return $value;
+ }
+ else
+ return null;
+ }
+ else
+ throw new TInvalidOperationException('map_readonly',get_class($this));
+ }
+ public function clear()
+ {
+ foreach(array_keys($this->_d) as $key)
+ $this->remove($key);
+ }
+ public function contains($key)
+ {
+ return isset($this->_d[$key]) || array_key_exists($key,$this->_d);
+ }
+ public function toArray()
+ {
+ return $this->_d;
+ }
+ public function copyFrom($data)
+ {
+ if(is_array($data) || $data instanceof Traversable)
+ {
+ if($this->getCount()>0)
+ $this->clear();
+ foreach($data as $key=>$value)
+ $this->add($key,$value);
+ }
+ else if($data!==null)
+ throw new TInvalidDataTypeException('map_data_not_iterable');
+ }
+ public function mergeWith($data)
+ {
+ if(is_array($data) || $data instanceof Traversable)
+ {
+ foreach($data as $key=>$value)
+ $this->add($key,$value);
+ }
+ else if($data!==null)
+ throw new TInvalidDataTypeException('map_data_not_iterable');
+ }
+ public function offsetExists($offset)
+ {
+ return $this->contains($offset);
+ }
+ public function offsetGet($offset)
+ {
+ return $this->itemAt($offset);
+ }
+ public function offsetSet($offset,$item)
+ {
+ $this->add($offset,$item);
+ }
+ public function offsetUnset($offset)
+ {
+ $this->remove($offset);
+ }
+}
+class TMapIterator implements Iterator
+{
+ private $_d;
+ private $_keys;
+ private $_key;
+ public function __construct(&$data)
+ {
+ $this->_d=&$data;
+ $this->_keys=array_keys($data);
+ }
+ public function rewind()
+ {
+ $this->_key=reset($this->_keys);
+ }
+ public function key()
+ {
+ return $this->_key;
+ }
+ public function current()
+ {
+ return $this->_d[$this->_key];
+ }
+ public function next()
+ {
+ $this->_key=next($this->_keys);
+ }
+ public function valid()
+ {
+ return $this->_key!==false;
+ }
+}
+class TStack extends TComponent implements IteratorAggregate,Countable
+{
+ private $_d=array();
+ private $_c=0;
+ public function __construct($data=null)
+ {
+ if($data!==null)
+ $this->copyFrom($data);
+ }
+ public function toArray()
+ {
+ return $this->_d;
+ }
+ public function copyFrom($data)
+ {
+ if(is_array($data) || ($data instanceof Traversable))
+ {
+ $this->clear();
+ foreach($data as $item)
+ {
+ $this->_d[]=$item;
+ ++$this->_c;
+ }
+ }
+ else if($data!==null)
+ throw new TInvalidDataTypeException('stack_data_not_iterable');
+ }
+ public function clear()
+ {
+ $this->_c=0;
+ $this->_d=array();
+ }
+ public function contains($item)
+ {
+ return array_search($item,$this->_d,true)!==false;
+ }
+ public function peek()
+ {
+ if($this->_c===0)
+ throw new TInvalidOperationException('stack_empty');
+ else
+ return $this->_d[$this->_c-1];
+ }
+ public function pop()
+ {
+ if($this->_c===0)
+ throw new TInvalidOperationException('stack_empty');
+ else
+ {
+ --$this->_c;
+ return array_pop($this->_d);
+ }
+ }
+ public function push($item)
+ {
+ ++$this->_c;
+ $this->_d[] = $item;
+ }
+ public function getIterator()
+ {
+ return new TStackIterator($this->_d);
+ }
+ public function getCount()
+ {
+ return $this->_c;
+ }
+ public function count()
+ {
+ return $this->getCount();
+ }
+}
+class TStackIterator implements Iterator
+{
+ private $_d;
+ private $_i;
+ private $_c;
+ public function __construct(&$data)
+ {
+ $this->_d=&$data;
+ $this->_i=0;
+ $this->_c=count($this->_d);
+ }
+ public function rewind()
+ {
+ $this->_i=0;
+ }
+ public function key()
+ {
+ return $this->_i;
+ }
+ public function current()
+ {
+ return $this->_d[$this->_i];
+ }
+ public function next()
+ {
+ $this->_i++;
+ }
+ public function valid()
+ {
+ return $this->_i<$this->_c;
+ }
+}
+class TXmlElement extends TComponent
+{
+ private $_parent=null;
+ private $_tagName='unknown';
+ private $_value='';
+ private $_elements=null;
+ private $_attributes=null;
+ public function __construct($tagName)
+ {
+ $this->setTagName($tagName);
+ }
+ public function getParent()
+ {
+ return $this->_parent;
+ }
+ public function setParent($parent)
+ {
+ $this->_parent=$parent;
+ }
+ public function getTagName()
+ {
+ return $this->_tagName;
+ }
+ public function setTagName($tagName)
+ {
+ $this->_tagName=$tagName;
+ }
+ public function getValue()
+ {
+ return $this->_value;
+ }
+ public function setValue($value)
+ {
+ $this->_value=$value;
+ }
+ public function getHasElement()
+ {
+ return $this->_elements!==null && $this->_elements->getCount()>0;
+ }
+ public function getHasAttribute()
+ {
+ return $this->_attributes!==null && $this->_attributes->getCount()>0;
+ }
+ public function getAttribute($name)
+ {
+ if($this->_attributes!==null)
+ return $this->_attributes->itemAt($name);
+ else
+ return null;
+ }
+ public function setAttribute($name,$value)
+ {
+ $this->getAttributes()->add($name,$value);
+ }
+ public function getElements()
+ {
+ if(!$this->_elements)
+ $this->_elements=new TXmlElementList($this);
+ return $this->_elements;
+ }
+ public function getAttributes()
+ {
+ if(!$this->_attributes)
+ $this->_attributes=new TMap;
+ return $this->_attributes;
+ }
+ public function getElementByTagName($tagName)
+ {
+ if($this->_elements)
+ {
+ foreach($this->_elements as $element)
+ if($element->_tagName===$tagName)
+ return $element;
+ }
+ return null;
+ }
+ public function getElementsByTagName($tagName)
+ {
+ $list=new TList;
+ if($this->_elements)
+ {
+ foreach($this->_elements as $element)
+ if($element->_tagName===$tagName)
+ $list->add($element);
+ }
+ return $list;
+ }
+ public function toString($indent=0)
+ {
+ $attr='';
+ if($this->_attributes!==null)
+ {
+ foreach($this->_attributes as $name=>$value)
+ {
+ $value=$this->xmlEncode($value);
+ $attr.=" $name=\"$value\"";
+ }
+ }
+ $prefix=str_repeat(' ',$indent*4);
+ if($this->getHasElement())
+ {
+ $str=$prefix."<{$this->_tagName}$attr>\n";
+ foreach($this->getElements() as $element)
+ $str.=$element->toString($indent+1)."\n";
+ $str.=$prefix."</{$this->_tagName}>";
+ return $str;
+ }
+ else if(($value=$this->getValue())!=='')
+ {
+ $value=$this->xmlEncode($value);
+ return $prefix."<{$this->_tagName}$attr>$value</{$this->_tagName}>";
+ }
+ else
+ return $prefix."<{$this->_tagName}$attr />";
+ }
+ public function __toString()
+ {
+ return $this->toString();
+ }
+ private function xmlEncode($str)
+ {
+ return strtr($str,array(
+ '>'=>'&gt;',
+ '<'=>'&lt;',
+ '&'=>'&amp;',
+ '"'=>'&quot;',
+ "\r"=>'&#xD;',
+ "\t"=>'&#x9;',
+ "\n"=>'&#xA;'));
+ }
+}
+class TXmlDocument extends TXmlElement
+{
+ private $_version;
+ private $_encoding;
+ public function __construct($version='1.0',$encoding='')
+ {
+ parent::__construct('');
+ $this->setVersion($version);
+ $this->setEncoding($encoding);
+ }
+ public function getVersion()
+ {
+ return $this->_version;
+ }
+ public function setVersion($version)
+ {
+ $this->_version=$version;
+ }
+ public function getEncoding()
+ {
+ return $this->_encoding;
+ }
+ public function setEncoding($encoding)
+ {
+ $this->_encoding=$encoding;
+ }
+ public function loadFromFile($file)
+ {
+ if(($str=@file_get_contents($file))!==false)
+ return $this->loadFromString($str);
+ else
+ throw new TIOException('xmldocument_file_read_failed',$file);
+ }
+ public function loadFromString($string)
+ {
+ $doc=new DOMDocument();
+ if($doc->loadXML($string)===false)
+ return false;
+ $this->setEncoding($doc->encoding);
+ $this->setVersion($doc->version);
+ $element=$doc->documentElement;
+ $this->setTagName($element->tagName);
+ $this->setValue($element->nodeValue);
+ $elements=$this->getElements();
+ $attributes=$this->getAttributes();
+ $elements->clear();
+ $attributes->clear();
+ static $bSimpleXml;
+ if($bSimpleXml === null)
+ $bSimpleXml = (boolean)function_exists('simplexml_load_string');
+ if($bSimpleXml)
+ {
+ $simpleDoc = simplexml_load_string($string);
+ $docNamespaces = $simpleDoc->getDocNamespaces(false);
+ $simpleDoc = null;
+ foreach($docNamespaces as $prefix => $uri)
+ {
+ if($prefix === '')
+ $attributes->add('xmlns', $uri);
+ else
+ $attributes->add('xmlns:'.$prefix, $uri);
+ }
+ }
+ foreach($element->attributes as $name=>$attr)
+ $attributes->add(($attr->prefix === '' ? '' : $attr->prefix . ':') .$name,$attr->value);
+ foreach($element->childNodes as $child)
+ {
+ if($child instanceof DOMElement)
+ $elements->add($this->buildElement($child));
+ }
+ return true;
+ }
+ public function saveToFile($file)
+ {
+ if(($fw=fopen($file,'w'))!==false)
+ {
+ fwrite($fw,$this->saveToString());
+ fclose($fw);
+ }
+ else
+ throw new TIOException('xmldocument_file_write_failed',$file);
+ }
+ public function saveToString()
+ {
+ $version=empty($this->_version)?' version="1.0"':' version="'.$this->_version.'"';
+ $encoding=empty($this->_encoding)?'':' encoding="'.$this->_encoding.'"';
+ return "<?xml{$version}{$encoding}?>\n".$this->toString(0);
+ }
+ public function __toString()
+ {
+ return $this->saveToString();
+ }
+ private function buildElement($node)
+ {
+ $element=new TXmlElement($node->tagName);
+ $element->setValue($node->nodeValue);
+ foreach($node->attributes as $name=>$attr)
+ $element->getAttributes()->add(($attr->prefix === '' ? '' : $attr->prefix . ':') . $name,$attr->value);
+ foreach($node->childNodes as $child)
+ {
+ if($child instanceof DOMElement)
+ $element->getElements()->add($this->buildElement($child));
+ }
+ return $element;
+ }
+}
+class TXmlElementList extends TList
+{
+ private $_o;
+ public function __construct(TXmlElement $owner)
+ {
+ $this->_o=$owner;
+ }
+ protected function getOwner()
+ {
+ return $this->_o;
+ }
+ public function insertAt($index,$item)
+ {
+ if($item instanceof TXmlElement)
+ {
+ parent::insertAt($index,$item);
+ if($item->getParent()!==null)
+ $item->getParent()->getElements()->remove($item);
+ $item->setParent($this->_o);
+ }
+ else
+ throw new TInvalidDataTypeException('xmlelementlist_xmlelement_required');
+ }
+ public function removeAt($index)
+ {
+ $item=parent::removeAt($index);
+ if($item instanceof TXmlElement)
+ $item->setParent(null);
+ return $item;
+ }
+}
+class TAuthorizationRule extends TComponent
+{
+ private $_action;
+ private $_users;
+ private $_roles;
+ private $_verb;
+ private $_ipRules;
+ private $_everyone;
+ private $_guest;
+ private $_authenticated;
+ public function __construct($action,$users,$roles,$verb='',$ipRules='')
+ {
+ $action=strtolower(trim($action));
+ if($action==='allow' || $action==='deny')
+ $this->_action=$action;
+ else
+ 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;
+ if(trim($users)==='')
+ $users='*';
+ foreach(explode(',',$users) as $user)
+ {
+ if(($user=trim(strtolower($user)))!=='')
+ {
+ if($user==='*')
+ {
+ $this->_everyone=true;
+ break;
+ }
+ else if($user==='?')
+ $this->_guest=true;
+ else if($user==='@')
+ $this->_authenticated=true;
+ else
+ $this->_users[]=$user;
+ }
+ }
+ if(trim($roles)==='')
+ $roles='*';
+ foreach(explode(',',$roles) as $role)
+ {
+ if(($role=trim(strtolower($role)))!=='')
+ $this->_roles[]=$role;
+ }
+ if(($verb=trim(strtolower($verb)))==='')
+ $verb='*';
+ if($verb==='*' || $verb==='get' || $verb==='post')
+ $this->_verb=$verb;
+ else
+ throw new TInvalidDataValueException('authorizationrule_verb_invalid',$verb);
+ if(trim($ipRules)==='')
+ $ipRules='*';
+ foreach(explode(',',$ipRules) as $ipRule)
+ {
+ if(($ipRule=trim($ipRule))!=='')
+ $this->_ipRules[]=$ipRule;
+ }
+ }
+ public function getAction()
+ {
+ return $this->_action;
+ }
+ public function getUsers()
+ {
+ return $this->_users;
+ }
+ public function getRoles()
+ {
+ return $this->_roles;
+ }
+ public function getVerb()
+ {
+ return $this->_verb;
+ }
+ public function getIPRules()
+ {
+ return $this->_ipRules;
+ }
+ public function getGuestApplied()
+ {
+ return $this->_guest || $this->_everyone;
+ }
+ public function getEveryoneApplied()
+ {
+ return $this->_everyone;
+ }
+ public function getAuthenticatedApplied()
+ {
+ return $this->_authenticated || $this->_everyone;
+ }
+ public function isUserAllowed(IUser $user,$verb,$ip)
+ {
+ if($this->isVerbMatched($verb) && $this->isIpMatched($ip) && $this->isUserMatched($user) && $this->isRoleMatched($user))
+ return ($this->_action==='allow')?1:-1;
+ else
+ return 0;
+ }
+ private function isIpMatched($ip)
+ {
+ if(empty($this->_ipRules))
+ return 1;
+ foreach($this->_ipRules as $rule)
+ {
+ if($rule==='*' || $rule===$ip || (($pos=strpos($rule,'*'))!==false && strncmp($ip,$rule,$pos)===0))
+ return 1;
+ }
+ return 0;
+ }
+ private function isUserMatched($user)
+ {
+ return ($this->_everyone || ($this->_guest && $user->getIsGuest()) || ($this->_authenticated && !$user->getIsGuest()) || in_array(strtolower($user->getName()),$this->_users));
+ }
+ private function isRoleMatched($user)
+ {
+ foreach($this->_roles as $role)
+ {
+ if($role==='*' || $user->isInRole($role))
+ return true;
+ }
+ return false;
+ }
+ private function isVerbMatched($verb)
+ {
+ return ($this->_verb==='*' || strcasecmp($verb,$this->_verb)===0);
+ }
+}
+class TAuthorizationRuleCollection extends TList
+{
+ public function isUserAllowed($user,$verb,$ip)
+ {
+ if($user instanceof IUser)
+ {
+ $verb=strtolower(trim($verb));
+ foreach($this as $rule)
+ {
+ if(($decision=$rule->isUserAllowed($user,$verb,$ip))!==0)
+ return ($decision>0);
+ }
+ return true;
+ }
+ else
+ return false;
+ }
+ public function insertAt($index,$item)
+ {
+ if($item instanceof TAuthorizationRule)
+ parent::insertAt($index,$item);
+ else
+ throw new TInvalidDataTypeException('authorizationrulecollection_authorizationrule_required');
+ }
+}
+class TSecurityManager extends TModule
+{
+ const STATE_VALIDATION_KEY='prado:securitymanager:validationkey';
+ const STATE_ENCRYPTION_KEY='prado:securitymanager:encryptionkey';
+ private $_validationKey=null;
+ private $_encryptionKey=null;
+ private $_validation=TSecurityManagerValidationMode::SHA1;
+ private $_encryption='3DES';
+ public function init($config)
+ {
+ $this->getApplication()->setSecurityManager($this);
+ }
+ protected function generateRandomKey()
+ {
+ return rand().rand().rand().rand();
+ }
+ public function getValidationKey()
+ {
+ if($this->_validationKey===null)
+ {
+ if(($this->_validationKey=$this->getApplication()->getGlobalState(self::STATE_VALIDATION_KEY))===null)
+ {
+ $this->_validationKey=$this->generateRandomKey();
+ $this->getApplication()->setGlobalState(self::STATE_VALIDATION_KEY,$this->_validationKey,null);
+ }
+ }
+ return $this->_validationKey;
+ }
+ public function setValidationKey($value)
+ {
+ if($value!=='')
+ $this->_validationKey=$value;
+ else
+ throw new TInvalidDataValueException('securitymanager_validationkey_invalid');
+ }
+ public function getEncryptionKey()
+ {
+ if($this->_encryptionKey===null)
+ {
+ if(($this->_encryptionKey=$this->getApplication()->getGlobalState(self::STATE_ENCRYPTION_KEY))===null)
+ {
+ $this->_encryptionKey=$this->generateRandomKey();
+ $this->getApplication()->setGlobalState(self::STATE_ENCRYPTION_KEY,$this->_encryptionKey,null);
+ }
+ }
+ return $this->_encryptionKey;
+ }
+ public function setEncryptionKey($value)
+ {
+ if($value!=='')
+ $this->_encryptionKey=$value;
+ else
+ throw new TInvalidDataValueException('securitymanager_encryptionkey_invalid');
+ }
+ public function getValidation()
+ {
+ return $this->_validation;
+ }
+ public function setValidation($value)
+ {
+ $this->_validation=TPropertyValue::ensureEnum($value,'TSecurityManagerValidationMode');
+ }
+ public function getEncryption()
+ {
+ return $this->_encryption;
+ }
+ public function setEncryption($value)
+ {
+ throw new TNotSupportedException('Currently only 3DES encryption is supported');
+ }
+ public function encrypt($data)
+ {
+ if(function_exists('mcrypt_encrypt'))
+ {
+ $module=mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_CBC, '');
+ $key=substr(md5($this->getEncryptionKey()),0,mcrypt_enc_get_key_size($module));
+ srand();
+ $iv=mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND);
+ mcrypt_generic_init($module,$key,$iv);
+ $encrypted=$iv.mcrypt_generic($module,$data);
+ mcrypt_generic_deinit($module);
+ mcrypt_module_close($module);
+ return $encrypted;
+ }
+ else
+ throw new TNotSupportedException('securitymanager_mcryptextension_required');
+ }
+ public function decrypt($data)
+ {
+ if(function_exists('mcrypt_decrypt'))
+ {
+ $module=mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_CBC, '');
+ $key=substr(md5($this->getEncryptionKey()),0,mcrypt_enc_get_key_size($module));
+ $ivSize=mcrypt_enc_get_iv_size($module);
+ $iv=substr($data,0,$ivSize);
+ mcrypt_generic_init($module,$key,$iv);
+ $decrypted=mdecrypt_generic($module,substr($data,$ivSize));
+ mcrypt_generic_deinit($module);
+ mcrypt_module_close($module);
+ return rtrim($decrypted,"\0");
+ }
+ else
+ throw new TNotSupportedException('securitymanager_mcryptextension_required');
+ }
+ public function hashData($data)
+ {
+ $hmac=$this->computeHMAC($data);
+ return $hmac.$data;
+ }
+ public function validateData($data)
+ {
+ $len=$this->_validation==='SHA1'?40:32;
+ if(strlen($data)>=$len)
+ {
+ $hmac=substr($data,0,$len);
+ $data2=substr($data,$len);
+ return $hmac===$this->computeHMAC($data2)?$data2:false;
+ }
+ else
+ return false;
+ }
+ protected function computeHMAC($data)
+ {
+ if($this->_validation==='SHA1')
+ {
+ $pack='H40';
+ $func='sha1';
+ }
+ else
+ {
+ $pack='H32';
+ $func='md5';
+ }
+ $key=$this->getValidationKey();
+ $key=str_pad($func($key), 64, chr(0));
+ return $func((str_repeat(chr(0x5C), 64) ^ substr($key, 0, 64)) . pack($pack, $func((str_repeat(chr(0x36), 64) ^ substr($key, 0, 64)) . $data)));
+ }
+}
+class TSecurityManagerValidationMode extends TEnumerable
+{
+ const MD5='MD5';
+ const SHA1='SHA1';
+}
+class THttpUtility
+{
+ private static $_encodeTable=array('<'=>'&lt;','>'=>'&gt;','"'=>'&quot;');
+ private static $_decodeTable=array('&lt;'=>'<','&gt;'=>'>','&quot;'=>'"');
+ public static function htmlEncode($s)
+ {
+ return strtr($s,self::$_encodeTable);
+ }
+ public static function htmlDecode($s)
+ {
+ return strtr($s,self::$_decodeTable);
+ }
+}
+class TJavaScript
+{
+ private static $_json;
+ public static function renderScriptFiles($files)
+ {
+ $str='';
+ foreach($files as $file)
+ $str.= self::renderScriptFile($file);
+ return $str;
+ }
+ public static function renderScriptFile($file)
+ {
+ return '<script type="text/javascript" src="'.THttpUtility::htmlEncode($file)."\"></script>\n";
+ }
+ public static function renderScriptBlocks($scripts)
+ {
+ if(count($scripts))
+ return "<script type=\"text/javascript\">\n/*<![CDATA[*/\n".implode("\n",$scripts)."\n/*]]>*/\n</script>\n";
+ else
+ return '';
+ }
+ public static function renderScriptBlock($script)
+ {
+ return "<script type=\"text/javascript\">\n/*<![CDATA[*/\n{$script}\n/*]]>*/\n</script>\n";
+ }
+ public static function quoteString($js,$forUrl=false)
+ {
+ if($forUrl)
+ return strtr($js,array('%'=>'%25',"\t"=>'\t',"\n"=>'\n',"\r"=>'\r','"'=>'\"','\''=>'\\\'','\\'=>'\\\\'));
+ else
+ return strtr($js,array("\t"=>'\t',"\n"=>'\n',"\r"=>'\r','"'=>'\"','\''=>'\\\'','\\'=>'\\\\'));
+ }
+ public static function quoteFunction($js)
+ {
+ if(self::isFunction($js))
+ return $js;
+ else
+ return 'javascript:'.$js;
+ }
+ public static function isFunction($js)
+ {
+ return preg_match('/^\s*javascript:/i', $js);
+ }
+ public static function encode($value,$toMap=true,$encodeEmptyStrings=false)
+ {
+ if(is_string($value))
+ {
+ if(($n=strlen($value))>2)
+ {
+ $first=$value[0];
+ $last=$value[$n-1];
+ if(($first==='[' && $last===']') || ($first==='{' && $last==='}'))
+ return $value;
+ }
+ if(self::isFunction($value))
+ return preg_replace('/^\s*javascript:/', '', $value);
+ else
+ return "'".self::quoteString($value)."'";
+ }
+ else if(is_bool($value))
+ return $value?'true':'false';
+ else if(is_array($value))
+ {
+ $results='';
+ if(($n=count($value))>0 && array_keys($value)!==range(0,$n-1))
+ {
+ foreach($value as $k=>$v)
+ {
+ if($v!=='' || $encodeEmptyStrings)
+ {
+ if($results!=='')
+ $results.=',';
+ $results.="'$k':".self::encode($v,$toMap,$encodeEmptyStrings);
+ }
+ }
+ return '{'.$results.'}';
+ }
+ else
+ {
+ foreach($value as $v)
+ {
+ if($v!=='' || $encodeEmptyStrings)
+ {
+ if($results!=='')
+ $results.=',';
+ $results.=self::encode($v,$toMap, $encodeEmptyStrings);
+ }
+ }
+ return '['.$results.']';
+ }
+ }
+ else if(is_integer($value))
+ return "$value";
+ else if(is_float($value))
+ {
+ if($value===-INF)
+ return 'Number.NEGATIVE_INFINITY';
+ else if($value===INF)
+ return 'Number.POSITIVE_INFINITY';
+ else
+ return "$value";
+ }
+ else if(is_object($value))
+ return self::encode(get_object_vars($value),$toMap);
+ else if($value===null)
+ return 'null';
+ else
+ return '';
+ }
+ public static function jsonEncode($value)
+ {
+ if(self::$_json === null)
+ self::$_json = Prado::createComponent('System.Web.Javascripts.TJSON');
+ return self::$_json->encode($value);
+ }
+ public static function jsonDecode($value)
+ {
+ if(self::$_json === null)
+ self::$_json = Prado::createComponent('System.Web.Javascripts.TJSON');
+ return self::$_json->decode($value);
+ }
+}
+class TUrlManager extends TModule
+{
+ public function constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems)
+ {
+ $url=$serviceID.'='.urlencode($serviceParam);
+ $amp=$encodeAmpersand?'&amp;':'&';
+ $request=$this->getRequest();
+ if(is_array($getItems) || $getItems instanceof Traversable)
+ {
+ if($encodeGetItems)
+ {
+ foreach($getItems as $name=>$value)
+ {
+ if(is_array($value))
+ {
+ $name=urlencode($name.'[]');
+ foreach($value as $v)
+ $url.=$amp.$name.'='.urlencode($v);
+ }
+ else
+ $url.=$amp.urlencode($name).'='.urlencode($value);
+ }
+ }
+ else
+ {
+ foreach($getItems as $name=>$value)
+ {
+ if(is_array($value))
+ {
+ foreach($value as $v)
+ $url.=$amp.$name.'[]='.$v;
+ }
+ else
+ $url.=$amp.$name.'='.$value;
+ }
+ }
+ }
+ if($request->getUrlFormat()===THttpRequestUrlFormat::Path)
+ return $request->getApplicationUrl().'/'.strtr($url,array($amp=>'/','?'=>'/','='=>$request->getUrlParamSeparator()));
+ else
+ return $request->getApplicationUrl().'?'.$url;
+ }
+ public function parseUrl()
+ {
+ $request=$this->getRequest();
+ $pathInfo=trim($request->getPathInfo(),'/');
+ if($request->getUrlFormat()===THttpRequestUrlFormat::Path && $pathInfo!=='')
+ {
+ $separator=$request->getUrlParamSeparator();
+ $paths=explode('/',$pathInfo);
+ $getVariables=array();
+ foreach($paths as $path)
+ {
+ if(($path=trim($path))!=='')
+ {
+ if(($pos=strpos($path,$separator))!==false)
+ {
+ $name=substr($path,0,$pos);
+ $value=substr($path,$pos+1);
+ if(($pos=strpos($name,'[]'))!==false)
+ $getVariables[substr($name,0,$pos)][]=$value;
+ else
+ $getVariables[$name]=$value;
+ }
+ else
+ $getVariables[$path]='';
+ }
+ }
+ return $getVariables;
+ }
+ else
+ return array();
+ }
+}
+class THttpRequest extends TApplicationComponent implements IteratorAggregate,ArrayAccess,Countable,IModule
+{
+ private $_urlManager=null;
+ private $_urlManagerID='';
+ private $_separator=',';
+ private $_serviceID=null;
+ private $_serviceParam=null;
+ private $_cookies=null;
+ private $_requestUri;
+ private $_pathInfo;
+ private $_cookieOnly=false;
+ private $_urlFormat=THttpRequestUrlFormat::Get;
+ private $_services;
+ private $_requestResolved=false;
+ private $_enableCookieValidation=false;
+ private $_url=null;
+ private $_id;
+ private $_items=array();
+ public function getID()
+ {
+ return $this->_id;
+ }
+ public function setID($value)
+ {
+ $this->_id=$value;
+ }
+ public function init($config)
+ {
+ if(empty($this->_urlManagerID))
+ {
+ $this->_urlManager=new TUrlManager;
+ $this->_urlManager->init(null);
+ }
+ else
+ {
+ $this->_urlManager=$this->getApplication()->getModule($this->_urlManagerID);
+ if($this->_urlManager===null)
+ throw new TConfigurationException('httprequest_urlmanager_inexist',$this->_urlManagerID);
+ if(!($this->_urlManager instanceof TUrlManager))
+ throw new TConfigurationException('httprequest_urlmanager_invalid',$this->_urlManagerID);
+ }
+ if(php_sapi_name()==='cli')
+ {
+ $_SERVER['REMOTE_ADDR']='127.0.0.1';
+ $_SERVER['REQUEST_METHOD']='GET';
+ $_SERVER['SERVER_NAME']='localhost';
+ $_SERVER['SERVER_PORT']=80;
+ $_SERVER['HTTP_USER_AGENT']='';
+ }
+ $this->_cookieOnly=(int)ini_get('session.use_cookies') && (int)ini_get('session.use_only_cookies');
+ if(isset($_SERVER['REQUEST_URI']))
+ $this->_requestUri=$_SERVER['REQUEST_URI'];
+ else $this->_requestUri=$_SERVER['SCRIPT_NAME'].(empty($_SERVER['QUERY_STRING'])?'':'?'.$_SERVER['QUERY_STRING']);
+ if(isset($_SERVER['PATH_INFO']))
+ $this->_pathInfo=$_SERVER['PATH_INFO'];
+ else if(strpos($_SERVER['PHP_SELF'],$_SERVER['SCRIPT_NAME'])===0 && $_SERVER['PHP_SELF']!==$_SERVER['SCRIPT_NAME'])
+ $this->_pathInfo=substr($_SERVER['PHP_SELF'],strlen($_SERVER['SCRIPT_NAME']));
+ else
+ $this->_pathInfo='';
+ if(get_magic_quotes_gpc())
+ {
+ if(isset($_GET))
+ $_GET=$this->stripSlashes($_GET);
+ if(isset($_POST))
+ $_POST=$this->stripSlashes($_POST);
+ if(isset($_REQUEST))
+ $_REQUEST=$this->stripSlashes($_REQUEST);
+ if(isset($_COOKIE))
+ $_COOKIE=$this->stripSlashes($_COOKIE);
+ }
+ $this->getApplication()->setRequest($this);
+ }
+ public function stripSlashes(&$data)
+ {
+ return is_array($data)?array_map(array($this,'stripSlashes'),$data):stripslashes($data);
+ }
+ public function getUrl()
+ {
+ if($this->_url===null)
+ {
+ $secure=$this->getIsSecureConnection();
+ $url=$secure?'https://':'http://';
+ if(empty($_SERVER['HTTP_HOST']))
+ {
+ $url.=$_SERVER['SERVER_NAME'];
+ $port=$_SERVER['SERVER_PORT'];
+ if(($port!=80 && !$secure) || ($port!=443 && $secure))
+ $url.=':'.$port;
+ }
+ else
+ $url.=$_SERVER['HTTP_HOST'];
+ $url.=$this->getRequestUri();
+ $this->_url=new TUri($url);
+ }
+ return $this->_url;
+ }
+ public function getUrlManager()
+ {
+ return $this->_urlManagerID;
+ }
+ public function setUrlManager($value)
+ {
+ $this->_urlManagerID=$value;
+ }
+ public function getUrlManagerModule()
+ {
+ return $this->_urlManager;
+ }
+ public function getUrlFormat()
+ {
+ return $this->_urlFormat;
+ }
+ public function setUrlFormat($value)
+ {
+ $this->_urlFormat=TPropertyValue::ensureEnum($value,'THttpRequestUrlFormat');
+ }
+ public function getUrlParamSeparator()
+ {
+ return $this->_separator;
+ }
+ public function setUrlParamSeparator($value)
+ {
+ if(strlen($value)===1)
+ $this->_separator=$value;
+ else
+ throw new TInvalidDataValueException('httprequest_separator_invalid');
+ }
+ public function getRequestType()
+ {
+ return $_SERVER['REQUEST_METHOD'];
+ }
+ public function getIsSecureConnection()
+ {
+ return isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'],'off');
+ }
+ public function getPathInfo()
+ {
+ return $this->_pathInfo;
+ }
+ public function getQueryString()
+ {
+ return isset($_SERVER['QUERY_STRING'])?$_SERVER['QUERY_STRING']:'';
+ }
+ public function getHttpProtocolVersion ()
+ {
+ return isset($_SERVER['SERVER_PROTOCOL'])?$_SERVER['SERVER_PROTOCOL']:'';
+ }
+ public function getRequestUri()
+ {
+ return $this->_requestUri;
+ }
+ public function getBaseUrl($forceSecureConnection=false)
+ {
+ $url=$this->getUrl();
+ $scheme=($forceSecureConnection)?"https":$url->getScheme();
+ $host=$url->getHost();
+ if (($port=$url->getPort())) $host.=':'.$port;
+ return $scheme.'://'.$host;
+ }
+ public function getApplicationUrl()
+ {
+ return $_SERVER['SCRIPT_NAME'];
+ }
+ public function getAbsoluteApplicationUrl($forceSecureConnection=false)
+ {
+ return $this->getBaseUrl($forceSecureConnection) . $this->getApplicationUrl();
+ }
+ public function getApplicationFilePath()
+ {
+ return realpath($_SERVER['SCRIPT_FILENAME']);
+ }
+ public function getServerName()
+ {
+ return $_SERVER['SERVER_NAME'];
+ }
+ public function getServerPort()
+ {
+ return $_SERVER['SERVER_PORT'];
+ }
+ public function getUrlReferrer()
+ {
+ return isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:null;
+ }
+ public function getBrowser()
+ {
+ try
+ {
+ return get_browser();
+ }
+ catch(TPhpErrorException $e)
+ {
+ throw new TConfigurationException('httprequest_browscap_required');
+ }
+ }
+ public function getUserAgent()
+ {
+ return $_SERVER['HTTP_USER_AGENT'];
+ }
+ public function getUserHostAddress()
+ {
+ return $_SERVER['REMOTE_ADDR'];
+ }
+ public function getUserHost()
+ {
+ return isset($_SERVER['REMOTE_HOST'])?$_SERVER['REMOTE_HOST']:null;
+ }
+ public function getAcceptTypes()
+ {
+ return $_SERVER['HTTP_ACCEPT'];
+ }
+ public function getUserLanguages()
+ {
+ return Prado::getUserLanguages();
+ }
+ public function getEnableCookieValidation()
+ {
+ return $this->_enableCookieValidation;
+ }
+ public function setEnableCookieValidation($value)
+ {
+ $this->_enableCookieValidation=TPropertyValue::ensureBoolean($value);
+ }
+ public function getCookies()
+ {
+ if($this->_cookies===null)
+ {
+ $this->_cookies=new THttpCookieCollection;
+ if($this->getEnableCookieValidation())
+ {
+ $sm=$this->getApplication()->getSecurityManager();
+ foreach($_COOKIE as $key=>$value)
+ {
+ if(($value=$sm->validateData($value))!==false)
+ $this->_cookies->add(new THttpCookie($key,$value));
+ }
+ }
+ else
+ {
+ foreach($_COOKIE as $key=>$value)
+ $this->_cookies->add(new THttpCookie($key,$value));
+ }
+ }
+ return $this->_cookies;
+ }
+ public function getUploadedFiles()
+ {
+ return $_FILES;
+ }
+ public function getServerVariables()
+ {
+ return $_SERVER;
+ }
+ public function getEnvironmentVariables()
+ {
+ return $_ENV;
+ }
+ public function constructUrl($serviceID,$serviceParam,$getItems=null,$encodeAmpersand=true,$encodeGetItems=true)
+ {
+ $url=$this->_urlManager->constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems);
+ if(defined('SID') && SID != '' && !$this->_cookieOnly)
+ return $url . (strpos($url,'?')===false? '?' : ($encodeAmpersand?'&amp;':'&')) . SID;
+ else
+ return $url;
+ }
+ protected function parseUrl()
+ {
+ return $this->_urlManager->parseUrl();
+ }
+ public function resolveRequest($serviceIDs)
+ {
+ $getParams=$this->parseUrl();
+ foreach($getParams as $name=>$value)
+ $_GET[$name]=$value;
+ $this->_items=array_merge($_GET,$_POST);
+ $this->_requestResolved=true;
+ foreach($serviceIDs as $serviceID)
+ {
+ if($this->contains($serviceID))
+ {
+ $this->setServiceID($serviceID);
+ $this->setServiceParameter($this->itemAt($serviceID));
+ return $serviceID;
+ }
+ }
+ return null;
+ }
+ public function getRequestResolved()
+ {
+ return $this->_requestResolved;
+ }
+ public function getServiceID()
+ {
+ return $this->_serviceID;
+ }
+ public function setServiceID($value)
+ {
+ $this->_serviceID=$value;
+ }
+ public function getServiceParameter()
+ {
+ return $this->_serviceParam;
+ }
+ public function setServiceParameter($value)
+ {
+ $this->_serviceParam=$value;
+ }
+ public function getIterator()
+ {
+ return new TMapIterator($this->_items);
+ }
+ public function getCount()
+ {
+ return count($this->_items);
+ }
+ public function count()
+ {
+ return $this->getCount();
+ }
+ public function getKeys()
+ {
+ return array_keys($this->_items);
+ }
+ public function itemAt($key)
+ {
+ return isset($this->_items[$key]) ? $this->_items[$key] : null;
+ }
+ public function add($key,$value)
+ {
+ $this->_items[$key]=$value;
+ }
+ public function remove($key)
+ {
+ if(isset($this->_items[$key]) || array_key_exists($key,$this->_items))
+ {
+ $value=$this->_items[$key];
+ unset($this->_items[$key]);
+ return $value;
+ }
+ else
+ return null;
+ }
+ public function clear()
+ {
+ foreach(array_keys($this->_items) as $key)
+ $this->remove($key);
+ }
+ public function contains($key)
+ {
+ return isset($this->_items[$key]) || array_key_exists($key,$this->_items);
+ }
+ public function toArray()
+ {
+ return $this->_items;
+ }
+ public function offsetExists($offset)
+ {
+ return $this->contains($offset);
+ }
+ public function offsetGet($offset)
+ {
+ return $this->itemAt($offset);
+ }
+ public function offsetSet($offset,$item)
+ {
+ $this->add($offset,$item);
+ }
+ public function offsetUnset($offset)
+ {
+ $this->remove($offset);
+ }
+}
+class THttpCookieCollection extends TList
+{
+ private $_o;
+ public function __construct($owner=null)
+ {
+ $this->_o=$owner;
+ }
+ public function insertAt($index,$item)
+ {
+ if($item instanceof THttpCookie)
+ {
+ parent::insertAt($index,$item);
+ if($this->_o instanceof THttpResponse)
+ $this->_o->addCookie($item);
+ }
+ else
+ throw new TInvalidDataTypeException('httpcookiecollection_httpcookie_required');
+ }
+ public function removeAt($index)
+ {
+ $item=parent::removeAt($index);
+ if($this->_o instanceof THttpResponse)
+ $this->_o->removeCookie($item);
+ return $item;
+ }
+ public function itemAt($index)
+ {
+ if(is_integer($index))
+ return parent::itemAt($index);
+ else
+ return $this->findCookieByName($index);
+ }
+ public function findCookieByName($name)
+ {
+ foreach($this as $cookie)
+ if($cookie->getName()===$name)
+ return $cookie;
+ return null;
+ }
+}
+class THttpCookie extends TComponent
+{
+ private $_domain='';
+ private $_name;
+ private $_value='';
+ private $_expire=0;
+ private $_path='/';
+ private $_secure=false;
+ public function __construct($name,$value)
+ {
+ $this->_name=$name;
+ $this->_value=$value;
+ }
+ public function getDomain()
+ {
+ return $this->_domain;
+ }
+ public function setDomain($value)
+ {
+ $this->_domain=$value;
+ }
+ public function getExpire()
+ {
+ return $this->_expire;
+ }
+ public function setExpire($value)
+ {
+ $this->_expire=TPropertyValue::ensureInteger($value);
+ }
+ public function getName()
+ {
+ return $this->_name;
+ }
+ public function setName($value)
+ {
+ $this->_name=$value;
+ }
+ public function getValue()
+ {
+ return $this->_value;
+ }
+ public function setValue($value)
+ {
+ $this->_value=$value;
+ }
+ public function getPath()
+ {
+ return $this->_path;
+ }
+ public function setPath($value)
+ {
+ $this->_path=$value;
+ }
+ public function getSecure()
+ {
+ return $this->_secure;
+ }
+ public function setSecure($value)
+ {
+ $this->_secure=TPropertyValue::ensureBoolean($value);
+ }
+}
+class TUri extends TComponent
+{
+ private static $_defaultPort=array(
+ 'ftp'=>21,
+ 'gopher'=>70,
+ 'http'=>80,
+ 'https'=>443,
+ 'news'=>119,
+ 'nntp'=>119,
+ 'wais'=>210,
+ 'telnet'=>23
+ );
+ private $_scheme;
+ private $_host;
+ private $_port;
+ private $_user;
+ private $_pass;
+ private $_path;
+ private $_query;
+ private $_fragment;
+ private $_uri;
+ public function __construct($uri)
+ {
+ if(($ret=@parse_url($uri))!==false)
+ {
+ $this->_scheme=isset($ret['scheme'])?$ret['scheme']:'';
+ $this->_host=isset($ret['host'])?$ret['host']:'';
+ $this->_port=isset($ret['port'])?$ret['port']:'';
+ $this->_user=isset($ret['user'])?$ret['user']:'';
+ $this->_pass=isset($ret['pass'])?$ret['pass']:'';
+ $this->_path=isset($ret['path'])?$ret['path']:'';
+ $this->_query=isset($ret['query'])?$ret['query']:'';
+ $this->_fragment=isset($ret['fragment'])?$ret['fragment']:'';
+ $this->_uri=$uri;
+ }
+ else
+ {
+ throw new TInvalidDataValueException('uri_format_invalid',$uri);
+ }
+ }
+ public function getUri()
+ {
+ return $this->_uri;
+ }
+ public function getScheme()
+ {
+ return $this->_scheme;
+ }
+ public function getHost()
+ {
+ return $this->_host;
+ }
+ public function getPort()
+ {
+ return $this->_port;
+ }
+ public function getUser()
+ {
+ return $this->_user;
+ }
+ public function getPassword()
+ {
+ return $this->_pass;
+ }
+ public function getPath()
+ {
+ return $this->_path;
+ }
+ public function getQuery()
+ {
+ return $this->_query;
+ }
+ public function getFragment()
+ {
+ return $this->_fragment;
+ }
+}
+class THttpRequestUrlFormat extends TEnumerable
+{
+ const Get='Get';
+ const Path='Path';
+}
+class THttpResponseAdapter extends TApplicationComponent
+{
+ private $_response;
+ public function __construct($response)
+ {
+ $this->_response=$response;
+ }
+ public function getResponse()
+ {
+ return $this->_response;
+ }
+ public function flushContent()
+ {
+ $this->_response->flushContent();
+ }
+ public function httpRedirect($url)
+ {
+ $this->_response->httpRedirect($url);
+ }
+ public function createNewHtmlWriter($type, $writer)
+ {
+ return $this->_response->createNewHtmlWriter($type,$writer);
+ }
+}
+class THttpResponse extends TModule implements ITextWriter
+{
+ private static $HTTP_STATUS_CODES = array(
+ 100 => 'Continue', 101 => 'Switching Protocols',
+ 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content',
+ 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect',
+ 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed',
+ 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported'
+ );
+ private $_bufferOutput=true;
+ private $_initialized=false;
+ private $_cookies=null;
+ private $_status=200;
+ private $_reason='OK';
+ private $_htmlWriterType='System.Web.UI.THtmlWriter';
+ private $_contentType=null;
+ private $_charset='';
+ private $_adapter;
+ public function __destruct()
+ {
+ }
+ public function setAdapter(THttpResponseAdapter $adapter)
+ {
+ $this->_adapter=$adapter;
+ }
+ public function getAdapter()
+ {
+ return $this->_adapter;
+ }
+ public function getHasAdapter()
+ {
+ return $this->_adapter!==null;
+ }
+ public function init($config)
+ {
+ if($this->_bufferOutput)
+ ob_start();
+ $this->_initialized=true;
+ $this->getApplication()->setResponse($this);
+ }
+ public function getCacheExpire()
+ {
+ return session_cache_expire();
+ }
+ public function setCacheExpire($value)
+ {
+ session_cache_expire(TPropertyValue::ensureInteger($value));
+ }
+ public function getCacheControl()
+ {
+ return session_cache_limiter();
+ }
+ public function setCacheControl($value)
+ {
+ session_cache_limiter(TPropertyValue::ensureEnum($value,array('none','nocache','private','private_no_expire','public')));
+ }
+ public function setContentType($type)
+ {
+ $this->_contentType = $type;
+ }
+ public function getContentType()
+ {
+ return $this->_contentType;
+ }
+ public function getCharset()
+ {
+ return $this->_charset;
+ }
+ public function setCharset($charset)
+ {
+ $this->_charset = $charset;
+ }
+ public function getBufferOutput()
+ {
+ return $this->_bufferOutput;
+ }
+ public function setBufferOutput($value)
+ {
+ if($this->_initialized)
+ throw new TInvalidOperationException('httpresponse_bufferoutput_unchangeable');
+ else
+ $this->_bufferOutput=TPropertyValue::ensureBoolean($value);
+ }
+ public function getStatusCode()
+ {
+ return $this->_status;
+ }
+ public function setStatusCode($status, $reason=null)
+ {
+ $status=TPropertyValue::ensureInteger($status);
+ if(isset(self::$HTTP_STATUS_CODES[$status])) {
+ $this->_reason=self::$HTTP_STATUS_CODES[$status];
+ }else{
+ if($reason===null || $reason==='') {
+ throw new TInvalidDataValueException("response_status_reason_missing");
+ }
+ $reason=TPropertyValue::ensureString($reason);
+ if(strpos($reason, "\r")!=false || strpos($reason, "\n")!=false) {
+ throw new TInvalidDataValueException("response_status_reason_barchars");
+ }
+ $this->_reason=$reason;
+ }
+ $this->_status=$status;
+ }
+ public function getStatusReason() {
+ return $this->_reason;
+ }
+ public function getCookies()
+ {
+ if($this->_cookies===null)
+ $this->_cookies=new THttpCookieCollection($this);
+ return $this->_cookies;
+ }
+ public function write($str)
+ {
+ echo $str;
+ }
+ public function writeFile($fileName,$content=null,$mimeType=null,$headers=null)
+ {
+ static $defaultMimeTypes=array(
+ 'css'=>'text/css',
+ 'gif'=>'image/gif',
+ 'jpg'=>'image/jpeg',
+ 'jpeg'=>'image/jpeg',
+ 'htm'=>'text/html',
+ 'html'=>'text/html',
+ 'js'=>'javascript/js',
+ 'pdf'=>'application/pdf',
+ 'xls'=>'application/vnd.ms-excel',
+ );
+ if($mimeType===null)
+ {
+ $mimeType='text/plain';
+ if(function_exists('mime_content_type'))
+ $mimeType=mime_content_type($fileName);
+ else if(($ext=strrchr($fileName,'.'))!==false)
+ {
+ $ext=substr($ext,1);
+ if(isset($defaultMimeTypes[$ext]))
+ $mimeType=$defaultMimeTypes[$ext];
+ }
+ }
+ $fn=basename($fileName);
+ $this->sendHttpHeader();
+ if(is_array($headers))
+ {
+ foreach($headers as $h)
+ header($h);
+ }
+ else
+ {
+ header('Pragma: public');
+ header('Expires: 0');
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+ }
+ header("Content-type: $mimeType");
+ header('Content-Length: '.($content===null?filesize($fileName):strlen($content)));
+ header("Content-Disposition: attachment; filename=\"$fn\"");
+ header('Content-Transfer-Encoding: binary');
+ if($content===null)
+ readfile($fileName);
+ else
+ echo $content;
+ }
+ public function redirect($url)
+ {
+ if($this->getHasAdapter())
+ $this->_adapter->httpRedirect($url);
+ else
+ $this->httpRedirect($url);
+ }
+ public function httpRedirect($url)
+ {
+ if(!$this->getApplication()->getRequestCompleted())
+ $this->getApplication()->onEndRequest();
+ if($url[0]==='/')
+ $url=$this->getRequest()->getBaseUrl().$url;
+ if ($this->_status >= 300 && $this->_status < 400)
+ header('Location: '.str_replace('&amp;','&',$url), true, $this->_status);
+ else
+ header('Location: '.str_replace('&amp;','&',$url));
+ exit();
+ }
+ public function reload()
+ {
+ $this->redirect($this->getRequest()->getRequestUri());
+ }
+ public function flush()
+ {
+ if($this->getHasAdapter())
+ $this->_adapter->flushContent();
+ else
+ $this->flushContent();
+ }
+ public function flushContent()
+ {
+ $this->sendHttpHeader();
+ $this->sendContentTypeHeader();
+ if($this->_bufferOutput)
+ ob_flush();
+ }
+ protected function sendHttpHeader ()
+ {
+ if (($version=$this->getRequest()->getHttpProtocolVersion())==='')
+ header (' ', true, $this->_status);
+ else
+ header($version.' '.$this->_status.' '.$this->_reason, true, $this->_status);
+ }
+ protected function sendContentTypeHeader()
+ {
+ $charset=$this->getCharset();
+ if($charset==='' && ($globalization=$this->getApplication()->getGlobalization(false))!==null)
+ $charset=$globalization->getCharset();
+ if($charset!=='')
+ {
+ $contentType=$this->_contentType===null?'text/html':$this->_contentType;
+ $this->appendHeader('Content-Type: '.$contentType.';charset='.$charset);
+ }
+ else if($this->_contentType!==null)
+ $this->appendHeader('Content-Type: '.$this->_contentType.';charset=UTF-8');
+ }
+ public function getContents()
+ {
+ return $this->_bufferOutput?ob_get_contents():'';
+ }
+ public function clear()
+ {
+ if($this->_bufferOutput)
+ ob_clean();
+ }
+ public function appendHeader($value)
+ {
+ header($value);
+ }
+ public function appendLog($message,$messageType=0,$destination='',$extraHeaders='')
+ {
+ error_log($message,$messageType,$destination,$extraHeaders);
+ }
+ public function addCookie($cookie)
+ {
+ $request=$this->getRequest();
+ if($request->getEnableCookieValidation())
+ {
+ $value=$this->getApplication()->getSecurityManager()->hashData($cookie->getValue());
+ setcookie($cookie->getName(),$value,$cookie->getExpire(),$cookie->getPath(),$cookie->getDomain(),$cookie->getSecure());
+ }
+ else
+ setcookie($cookie->getName(),$cookie->getValue(),$cookie->getExpire(),$cookie->getPath(),$cookie->getDomain(),$cookie->getSecure());
+ }
+ public function removeCookie($cookie)
+ {
+ setcookie($cookie->getName(),null,0,$cookie->getPath(),$cookie->getDomain(),$cookie->getSecure());
+ }
+ public function getHtmlWriterType()
+ {
+ return $this->_htmlWriterType;
+ }
+ public function setHtmlWriterType($value)
+ {
+ $this->_htmlWriterType=$value;
+ }
+ public function createHtmlWriter($type=null)
+ {
+ if($type===null)
+ $type=$this->getHtmlWriterType();
+ if($this->getHasAdapter())
+ return $this->_adapter->createNewHtmlWriter($type, $this);
+ else
+ return $this->createNewHtmlWriter($type, $this);
+ }
+ public function createNewHtmlWriter($type, $writer)
+ {
+ return Prado::createComponent($type, $writer);
+ }
+}
+class THttpSession extends TApplicationComponent implements IteratorAggregate,ArrayAccess,Countable,IModule
+{
+ private $_initialized=false;
+ private $_started=false;
+ private $_autoStart=false;
+ private $_cookie=null;
+ private $_id;
+ private $_customStorage=false;
+ public function getID()
+ {
+ return $this->_id;
+ }
+ public function setID($value)
+ {
+ $this->_id=$value;
+ }
+ public function init($config)
+ {
+ if($this->_autoStart)
+ $this->open();
+ $this->_initialized=true;
+ $this->getApplication()->setSession($this);
+ register_shutdown_function(array($this, "close"));
+ }
+ public function open()
+ {
+ if(!$this->_started)
+ {
+ if($this->_customStorage)
+ session_set_save_handler(array($this,'_open'),array($this,'_close'),array($this,'_read'),array($this,'_write'),array($this,'_destroy'),array($this,'_gc'));
+ if($this->_cookie!==null)
+ session_set_cookie_params($this->_cookie->getExpire(),$this->_cookie->getPath(),$this->_cookie->getDomain(),$this->_cookie->getSecure());
+ if(ini_get('session.auto_start')!=='1')
+ session_start();
+ $this->_started=true;
+ }
+ }
+ public function close()
+ {
+ if($this->_started)
+ {
+ session_write_close();
+ $this->_started=false;
+ }
+ }
+ public function destroy()
+ {
+ if($this->_started)
+ {
+ session_destroy();
+ $this->_started=false;
+ }
+ }
+ public function getIsStarted()
+ {
+ return $this->_started;
+ }
+ public function getSessionID()
+ {
+ return session_id();
+ }
+ public function setSessionID($value)
+ {
+ if($this->_started)
+ throw new TInvalidOperationException('httpsession_sessionid_unchangeable');
+ else
+ session_id($value);
+ }
+ public function getSessionName()
+ {
+ return session_name();
+ }
+ public function setSessionName($value)
+ {
+ if($this->_started)
+ throw new TInvalidOperationException('httpsession_sessionname_unchangeable');
+ else if(ctype_alnum($value))
+ session_name($value);
+ else
+ throw new TInvalidDataValueException('httpsession_sessionname_invalid',$value);
+ }
+ public function getSavePath()
+ {
+ return session_save_path();
+ }
+ public function setSavePath($value)
+ {
+ if($this->_started)
+ throw new TInvalidOperationException('httpsession_savepath_unchangeable');
+ else if(is_dir($value))
+ session_save_path($value);
+ else
+ throw new TInvalidDataValueException('httpsession_savepath_invalid',$value);
+ }
+ public function getUseCustomStorage()
+ {
+ return $this->_customStorage;
+ }
+ public function setUseCustomStorage($value)
+ {
+ $this->_customStorage=TPropertyValue::ensureBoolean($value);
+ }
+ public function getCookie()
+ {
+ if($this->_cookie===null)
+ $this->_cookie=new THttpCookie($this->getSessionName(),$this->getSessionID());
+ return $this->_cookie;
+ }
+ public function getCookieMode()
+ {
+ if(ini_get('session.use_cookies')==='0')
+ return THttpSessionCookieMode::None;
+ else if(ini_get('session.use_only_cookies')==='0')
+ return THttpSessionCookieMode::Allow;
+ else
+ return THttpSessionCookieMode::Only;
+ }
+ public function setCookieMode($value)
+ {
+ if($this->_started)
+ throw new TInvalidOperationException('httpsession_cookiemode_unchangeable');
+ else
+ {
+ $value=TPropertyValue::ensureEnum($value,'THttpSessionCookieMode');
+ if($value===THttpSessionCookieMode::None)
+ ini_set('session.use_cookies','0');
+ else if($value===THttpSessionCookieMode::Allow)
+ {
+ ini_set('session.use_cookies','1');
+ ini_set('session.use_only_cookies','0');
+ }
+ else
+ {
+ ini_set('session.use_cookies','1');
+ ini_set('session.use_only_cookies','1');
+ }
+ }
+ }
+ public function getAutoStart()
+ {
+ return $this->_autoStart;
+ }
+ public function setAutoStart($value)
+ {
+ if($this->_initialized)
+ throw new TInvalidOperationException('httpsession_autostart_unchangeable');
+ else
+ $this->_autoStart=TPropertyValue::ensureBoolean($value);
+ }
+ public function getGCProbability()
+ {
+ return TPropertyValue::ensureInteger(ini_get('session.gc_probability'));
+ }
+ public function setGCProbability($value)
+ {
+ if($this->_started)
+ throw new TInvalidOperationException('httpsession_gcprobability_unchangeable');
+ else
+ {
+ $value=TPropertyValue::ensureInteger($value);
+ if($value>=0 && $value<=100)
+ {
+ ini_set('session.gc_probability',$value);
+ ini_set('session.gc_divisor','100');
+ }
+ else
+ throw new TInvalidDataValueException('httpsession_gcprobability_invalid',$value);
+ }
+ }
+ public function getUseTransparentSessionID()
+ {
+ return ini_get('session.use_trans_sid')==='1';
+ }
+ public function setUseTransparentSessionID($value)
+ {
+ if($this->_started)
+ throw new TInvalidOperationException('httpsession_transid_unchangeable');
+ else
+ ini_set('session.use_trans_sid',TPropertyValue::ensureBoolean($value)?'1':'0');
+ }
+ public function getTimeout()
+ {
+ return TPropertyValue::ensureInteger(ini_get('session.gc_maxlifetime'));
+ }
+ public function setTimeout($value)
+ {
+ if($this->_started)
+ throw new TInvalidOperationException('httpsession_maxlifetime_unchangeable');
+ else
+ ini_set('session.gc_maxlifetime',$value);
+ }
+ public function _open($savePath,$sessionName)
+ {
+ return true;
+ }
+ public function _close()
+ {
+ return true;
+ }
+ public function _read($id)
+ {
+ return '';
+ }
+ public function _write($id,$data)
+ {
+ return true;
+ }
+ public function _destroy($id)
+ {
+ return true;
+ }
+ public function _gc($maxLifetime)
+ {
+ return true;
+ }
+ public function getIterator()
+ {
+ return new TSessionIterator;
+ }
+ public function getCount()
+ {
+ return count($_SESSION);
+ }
+ public function count()
+ {
+ return $this->getCount();
+ }
+ public function getKeys()
+ {
+ return array_keys($_SESSION);
+ }
+ public function itemAt($key)
+ {
+ return isset($_SESSION[$key]) ? $_SESSION[$key] : null;
+ }
+ public function add($key,$value)
+ {
+ $_SESSION[$key]=$value;
+ }
+ public function remove($key)
+ {
+ if(isset($_SESSION[$key]))
+ {
+ $value=$_SESSION[$key];
+ unset($_SESSION[$key]);
+ return $value;
+ }
+ else
+ return null;
+ }
+ public function clear()
+ {
+ foreach(array_keys($_SESSION) as $key)
+ unset($_SESSION[$key]);
+ }
+ public function contains($key)
+ {
+ return isset($_SESSION[$key]);
+ }
+ public function toArray()
+ {
+ return $_SESSION;
+ }
+ public function offsetExists($offset)
+ {
+ return isset($_SESSION[$offset]);
+ }
+ public function offsetGet($offset)
+ {
+ return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null;
+ }
+ public function offsetSet($offset,$item)
+ {
+ $_SESSION[$offset]=$item;
+ }
+ public function offsetUnset($offset)
+ {
+ unset($_SESSION[$offset]);
+ }
+}
+class TSessionIterator implements Iterator
+{
+ private $_keys;
+ private $_key;
+ public function __construct()
+ {
+ $this->_keys=array_keys($_SESSION);
+ }
+ public function rewind()
+ {
+ $this->_key=reset($this->_keys);
+ }
+ public function key()
+ {
+ return $this->_key;
+ }
+ public function current()
+ {
+ return isset($_SESSION[$this->_key])?$_SESSION[$this->_key]:null;
+ }
+ public function next()
+ {
+ do
+ {
+ $this->_key=next($this->_keys);
+ }
+ while(!isset($_SESSION[$this->_key]) && $this->_key!==false);
+ }
+ public function valid()
+ {
+ return $this->_key!==false;
+ }
+}
+class THttpSessionCookieMode extends TEnumerable
+{
+ const None='None';
+ const Allow='Allow';
+ const Only='Only';
+}
+Prado::using('System.Web.UI.WebControls.*');
+class TAttributeCollection extends TMap
+{
+ private $_caseSensitive=false;
+ public function __get($name)
+ {
+ return $this->contains($name)?$this->itemAt($name):parent::__get($name);
+ }
+ public function __set($name,$value)
+ {
+ $this->add($name,$value);
+ }
+ public function getCaseSensitive()
+ {
+ return $this->_caseSensitive;
+ }
+ public function setCaseSensitive($value)
+ {
+ $this->_caseSensitive=TPropertyValue::ensureBoolean($value);
+ }
+ public function itemAt($key)
+ {
+ return parent::itemAt($this->_caseSensitive?$key:strtolower($key));
+ }
+ public function add($key,$value)
+ {
+ parent::add($this->_caseSensitive?$key:strtolower($key),$value);
+ }
+ public function remove($key)
+ {
+ return parent::remove($this->_caseSensitive?$key:strtolower($key));
+ }
+ public function contains($key)
+ {
+ return parent::contains($this->_caseSensitive?$key:strtolower($key));
+ }
+ public function hasProperty($name)
+ {
+ return $this->contains($name) || parent::hasProperty($name);
+ }
+ public function canGetProperty($name)
+ {
+ return $this->contains($name) || parent::canGetProperty($name);
+ }
+ public function canSetProperty($name)
+ {
+ return true;
+ }
+}
+class TControlAdapter extends TApplicationComponent
+{
+ private $_control;
+ public function __construct($control)
+ {
+ $this->_control=$control;
+ }
+ public function getControl()
+ {
+ return $this->_control;
+ }
+ public function getPage()
+ {
+ return $this->_control?$this->_control->getPage():null;
+ }
+ public function createChildControls()
+ {
+ $this->_control->createChildControls();
+ }
+ public function loadState()
+ {
+ $this->_control->loadState();
+ }
+ public function saveState()
+ {
+ $this->_control->saveState();
+ }
+ public function onInit($param)
+ {
+ $this->_control->onInit($param);
+ }
+ public function onLoad($param)
+ {
+ $this->_control->onLoad($param);
+ }
+ public function onPreRender($param)
+ {
+ $this->_control->onPreRender($param);
+ }
+ public function onUnload($param)
+ {
+ $this->_control->onUnload($param);
+ }
+ public function render($writer)
+ {
+ $this->_control->render($writer);
+ }
+ public function renderChildren($writer)
+ {
+ $this->_control->renderChildren($writer);
+ }
+}
+class TControl extends TApplicationComponent implements IRenderable, IBindable
+{
+ const ID_FORMAT='/^[a-zA-Z_]\\w*$/';
+ const ID_SEPARATOR='$';
+ const CLIENT_ID_SEPARATOR='_';
+ const AUTOMATIC_ID_PREFIX='ctl';
+ const CS_CONSTRUCTED=0;
+ const CS_CHILD_INITIALIZED=1;
+ const CS_INITIALIZED=2;
+ const CS_STATE_LOADED=3;
+ const CS_LOADED=4;
+ const CS_PRERENDERED=5;
+ const IS_ID_SET=0x01;
+ const IS_DISABLE_VIEWSTATE=0x02;
+ const IS_SKIN_APPLIED=0x04;
+ const IS_STYLESHEET_APPLIED=0x08;
+ const IS_DISABLE_THEMING=0x10;
+ const IS_CHILD_CREATED=0x20;
+ const IS_CREATING_CHILD=0x40;
+ const RF_CONTROLS=0;
+ const RF_CHILD_STATE=1;
+ const RF_NAMED_CONTROLS=2;
+ const RF_NAMED_CONTROLS_ID=3;
+ const RF_SKIN_ID=4;
+ const RF_DATA_BINDINGS=5;
+ const RF_EVENTS=6;
+ const RF_CONTROLSTATE=7;
+ const RF_NAMED_OBJECTS=8;
+ const RF_ADAPTER=9;
+ const RF_AUTO_BINDINGS=10;
+ private $_id='';
+ private $_uid;
+ private $_parent;
+ private $_page;
+ private $_namingContainer;
+ private $_tplControl;
+ private $_viewState=array();
+ private $_tempState=array();
+ private $_trackViewState=true;
+ private $_stage=0;
+ private $_flags=0;
+ private $_rf=array();
+ public function __construct()
+ {
+ }
+ public function __get($name)
+ {
+ if(isset($this->_rf[self::RF_NAMED_OBJECTS][$name]))
+ return $this->_rf[self::RF_NAMED_OBJECTS][$name];
+ else
+ return parent::__get($name);
+ }
+ public function getHasAdapter()
+ {
+ return isset($this->_rf[self::RF_ADAPTER]);
+ }
+ public function getAdapter()
+ {
+ return isset($this->_rf[self::RF_ADAPTER])?$this->_rf[self::RF_ADAPTER]:null;
+ }
+ public function setAdapter(TControlAdapter $adapter)
+ {
+ $this->_rf[self::RF_ADAPTER]=$adapter;
+ }
+ public function getParent()
+ {
+ return $this->_parent;
+ }
+ public function getNamingContainer()
+ {
+ if(!$this->_namingContainer && $this->_parent)
+ {
+ if($this->_parent instanceof INamingContainer)
+ $this->_namingContainer=$this->_parent;
+ else
+ $this->_namingContainer=$this->_parent->getNamingContainer();
+ }
+ return $this->_namingContainer;
+ }
+ public function getPage()
+ {
+ if(!$this->_page)
+ {
+ if($this->_parent)
+ $this->_page=$this->_parent->getPage();
+ else if($this->_tplControl)
+ $this->_page=$this->_tplControl->getPage();
+ }
+ return $this->_page;
+ }
+ public function setPage($page)
+ {
+ $this->_page=$page;
+ }
+ public function setTemplateControl($control)
+ {
+ $this->_tplControl=$control;
+ }
+ public function getTemplateControl()
+ {
+ if(!$this->_tplControl && $this->_parent)
+ $this->_tplControl=$this->_parent->getTemplateControl();
+ return $this->_tplControl;
+ }
+ public function getSourceTemplateControl()
+ {
+ $control=$this;
+ while(($control instanceof TControl) && ($control=$control->getTemplateControl())!==null)
+ {
+ if(($control instanceof TTemplateControl) && $control->getIsSourceTemplateControl())
+ return $control;
+ }
+ return $this->getPage();
+ }
+ protected function getControlStage()
+ {
+ return $this->_stage;
+ }
+ protected function setControlStage($value)
+ {
+ $this->_stage=$value;
+ }
+ public function getID($hideAutoID=true)
+ {
+ if($hideAutoID)
+ return ($this->_flags & self::IS_ID_SET) ? $this->_id : '';
+ else
+ return $this->_id;
+ }
+ public function setID($id)
+ {
+ if(!preg_match(self::ID_FORMAT,$id))
+ throw new TInvalidDataValueException('control_id_invalid',get_class($this),$id);
+ $this->_id=$id;
+ $this->_flags |= self::IS_ID_SET;
+ $this->clearCachedUniqueID($this instanceof INamingContainer);
+ if($this->_namingContainer)
+ $this->_namingContainer->clearNameTable();
+ }
+ public function getUniqueID()
+ {
+ if($this->_uid==='' || $this->_uid===null)
+ {
+ $this->_uid='';
+ if($namingContainer=$this->getNamingContainer())
+ {
+ if($this->getPage()===$namingContainer)
+ return ($this->_uid=$this->_id);
+ else if(($prefix=$namingContainer->getUniqueID())==='')
+ return $this->_id;
+ else
+ return ($this->_uid=$prefix.self::ID_SEPARATOR.$this->_id);
+ }
+ else
+ return $this->_id;
+ }
+ else
+ return $this->_uid;
+ }
+ public function focus()
+ {
+ $this->getPage()->setFocus($this);
+ }
+ public function getClientID()
+ {
+ return strtr($this->getUniqueID(),self::ID_SEPARATOR,self::CLIENT_ID_SEPARATOR);
+ }
+ public static function convertUniqueIdToClientId($uniqueID)
+ {
+ return strtr($uniqueID,self::ID_SEPARATOR,self::CLIENT_ID_SEPARATOR);
+ }
+ public function getSkinID()
+ {
+ return isset($this->_rf[self::RF_SKIN_ID])?$this->_rf[self::RF_SKIN_ID]:'';
+ }
+ public function setSkinID($value)
+ {
+ if(($this->_flags & self::IS_SKIN_APPLIED) || $this->_stage>=self::CS_CHILD_INITIALIZED)
+ throw new TInvalidOperationException('control_skinid_unchangeable',get_class($this));
+ else
+ $this->_rf[self::RF_SKIN_ID]=$value;
+ }
+ public function getEnableTheming()
+ {
+ if($this->_flags & self::IS_DISABLE_THEMING)
+ return false;
+ else
+ return $this->_parent?$this->_parent->getEnableTheming():true;
+ }
+ public function setEnableTheming($value)
+ {
+ if($this->_stage>=self::CS_CHILD_INITIALIZED)
+ throw new TInvalidOperationException('control_enabletheming_unchangeable',get_class($this),$this->getUniqueID());
+ else if(TPropertyValue::ensureBoolean($value))
+ $this->_flags &= ~self::IS_DISABLE_THEMING;
+ else
+ $this->_flags |= self::IS_DISABLE_THEMING;
+ }
+ public function getCustomData()
+ {
+ return $this->getViewState('CustomData',null);
+ }
+ public function setCustomData($value)
+ {
+ $this->setViewState('CustomData',$value,null);
+ }
+ public function getHasControls()
+ {
+ return isset($this->_rf[self::RF_CONTROLS]) && $this->_rf[self::RF_CONTROLS]->getCount()>0;
+ }
+ public function getControls()
+ {
+ if(!isset($this->_rf[self::RF_CONTROLS]))
+ $this->_rf[self::RF_CONTROLS]=$this->createControlCollection();
+ return $this->_rf[self::RF_CONTROLS];
+ }
+ protected function createControlCollection()
+ {
+ return $this->getAllowChildControls()?new TControlCollection($this):new TEmptyControlCollection($this);
+ }
+ public function getVisible($checkParents=true)
+ {
+ if($checkParents)
+ {
+ for($control=$this;$control;$control=$control->_parent)
+ if(!$control->getVisible(false))
+ return false;
+ return true;
+ }
+ else
+ return $this->getViewState('Visible',true);
+ }
+ public function setVisible($value)
+ {
+ $this->setViewState('Visible',TPropertyValue::ensureBoolean($value),true);
+ }
+ public function getEnabled($checkParents=false)
+ {
+ if($checkParents)
+ {
+ for($control=$this;$control;$control=$control->_parent)
+ if(!$control->getViewState('Enabled',true))
+ return false;
+ return true;
+ }
+ else
+ return $this->getViewState('Enabled',true);
+ }
+ public function setEnabled($value)
+ {
+ $this->setViewState('Enabled',TPropertyValue::ensureBoolean($value),true);
+ }
+ public function getHasAttributes()
+ {
+ if($attributes=$this->getViewState('Attributes',null))
+ return $attributes->getCount()>0;
+ else
+ return false;
+ }
+ public function getAttributes()
+ {
+ if($attributes=$this->getViewState('Attributes',null))
+ return $attributes;
+ else
+ {
+ $attributes=new TAttributeCollection;
+ $this->setViewState('Attributes',$attributes,null);
+ return $attributes;
+ }
+ }
+ public function hasAttribute($name)
+ {
+ if($attributes=$this->getViewState('Attributes',null))
+ return $attributes->contains($name);
+ else
+ return false;
+ }
+ public function getAttribute($name)
+ {
+ if($attributes=$this->getViewState('Attributes',null))
+ return $attributes->itemAt($name);
+ else
+ return null;
+ }
+ public function setAttribute($name,$value)
+ {
+ $this->getAttributes()->add($name,$value);
+ }
+ public function removeAttribute($name)
+ {
+ if($attributes=$this->getViewState('Attributes',null))
+ return $attributes->remove($name);
+ else
+ return null;
+ }
+ public function getEnableViewState($checkParents=false)
+ {
+ if($checkParents)
+ {
+ for($control=$this;$control!==null;$control=$control->getParent())
+ if($control->_flags & self::IS_DISABLE_VIEWSTATE)
+ return false;
+ return true;
+ }
+ else
+ return !($this->_flags & self::IS_DISABLE_VIEWSTATE);
+ }
+ public function setEnableViewState($value)
+ {
+ if(TPropertyValue::ensureBoolean($value))
+ $this->_flags &= ~self::IS_DISABLE_VIEWSTATE;
+ else
+ $this->_flags |= self::IS_DISABLE_VIEWSTATE;
+ }
+ protected function getControlState($key,$defaultValue=null)
+ {
+ return isset($this->_rf[self::RF_CONTROLSTATE][$key])?$this->_rf[self::RF_CONTROLSTATE][$key]:$defaultValue;
+ }
+ protected function setControlState($key,$value,$defaultValue=null)
+ {
+ if($value===$defaultValue)
+ unset($this->_rf[self::RF_CONTROLSTATE][$key]);
+ else
+ $this->_rf[self::RF_CONTROLSTATE][$key]=$value;
+ }
+ protected function clearControlState($key)
+ {
+ unset($this->_rf[self::RF_CONTROLSTATE][$key]);
+ }
+ public function trackViewState($enabled)
+ {
+ $this->_trackViewState=TPropertyValue::ensureBoolean($enabled);
+ }
+ public function getViewState($key,$defaultValue=null)
+ {
+ if(isset($this->_viewState[$key]))
+ return $this->_viewState[$key]!==null?$this->_viewState[$key]:$defaultValue;
+ else if(isset($this->_tempState[$key]))
+ {
+ if(is_object($this->_tempState[$key]) && $this->_trackViewState)
+ $this->_viewState[$key]=$this->_tempState[$key];
+ return $this->_tempState[$key];
+ }
+ else
+ return $defaultValue;
+ }
+ public function setViewState($key,$value,$defaultValue=null)
+ {
+ if($this->_trackViewState)
+ {
+ $this->_viewState[$key]=$value;
+ unset($this->_tempState[$key]);
+ }
+ else
+ {
+ unset($this->_viewState[$key]);
+ $this->_tempState[$key]=$value;
+ }
+ }
+ public function clearViewState($key)
+ {
+ unset($this->_viewState[$key]);
+ unset($this->_tempState[$key]);
+ }
+ public function bindProperty($name,$expression)
+ {
+ $this->_rf[self::RF_DATA_BINDINGS][$name]=$expression;
+ }
+ public function unbindProperty($name)
+ {
+ unset($this->_rf[self::RF_DATA_BINDINGS][$name]);
+ }
+ public function autoBindProperty($name,$expression)
+ {
+ $this->_rf[self::RF_AUTO_BINDINGS][$name]=$expression;
+ }
+ public function dataBind()
+ {
+ $this->dataBindProperties();
+ $this->onDataBinding(null);
+ $this->dataBindChildren();
+ }
+ protected function dataBindProperties()
+ {
+ if(isset($this->_rf[self::RF_DATA_BINDINGS]))
+ {
+ if(($context=$this->getTemplateControl())===null)
+ $context=$this;
+ foreach($this->_rf[self::RF_DATA_BINDINGS] as $property=>$expression)
+ $this->setSubProperty($property,$context->evaluateExpression($expression));
+ }
+ }
+ protected function autoDataBindProperties()
+ {
+ if(isset($this->_rf[self::RF_AUTO_BINDINGS]))
+ {
+ if(($context=$this->getTemplateControl())===null)
+ $context=$this;
+ foreach($this->_rf[self::RF_AUTO_BINDINGS] as $property=>$expression)
+ $this->setSubProperty($property,$context->evaluateExpression($expression));
+ }
+ }
+ protected function dataBindChildren()
+ {
+ if(isset($this->_rf[self::RF_CONTROLS]))
+ {
+ foreach($this->_rf[self::RF_CONTROLS] as $control)
+ if($control instanceof IBindable)
+ $control->dataBind();
+ }
+ }
+ final protected function getChildControlsCreated()
+ {
+ return ($this->_flags & self::IS_CHILD_CREATED)!==0;
+ }
+ final protected function setChildControlsCreated($value)
+ {
+ if($value)
+ $this->_flags |= self::IS_CHILD_CREATED;
+ else
+ {
+ if($this->getHasControls() && ($this->_flags & self::IS_CHILD_CREATED))
+ $this->getControls()->clear();
+ $this->_flags &= ~self::IS_CHILD_CREATED;
+ }
+ }
+ public function ensureChildControls()
+ {
+ if(!($this->_flags & self::IS_CHILD_CREATED) && !($this->_flags & self::IS_CREATING_CHILD))
+ {
+ try
+ {
+ $this->_flags |= self::IS_CREATING_CHILD;
+ if(isset($this->_rf[self::RF_ADAPTER]))
+ $this->_rf[self::RF_ADAPTER]->createChildControls();
+ else
+ $this->createChildControls();
+ $this->_flags &= ~self::IS_CREATING_CHILD;
+ $this->_flags |= self::IS_CHILD_CREATED;
+ }
+ catch(Exception $e)
+ {
+ $this->_flags &= ~self::IS_CREATING_CHILD;
+ $this->_flags |= self::IS_CHILD_CREATED;
+ throw $e;
+ }
+ }
+ }
+ public function createChildControls()
+ {
+ }
+ public function findControl($id)
+ {
+ $id=strtr($id,'.',self::ID_SEPARATOR);
+ $container=($this instanceof INamingContainer)?$this:$this->getNamingContainer();
+ if(!$container || !$container->getHasControls())
+ return null;
+ if(!isset($container->_rf[self::RF_NAMED_CONTROLS]))
+ {
+ $container->_rf[self::RF_NAMED_CONTROLS]=array();
+ $container->fillNameTable($container,$container->_rf[self::RF_CONTROLS]);
+ }
+ if(($pos=strpos($id,self::ID_SEPARATOR))===false)
+ return isset($container->_rf[self::RF_NAMED_CONTROLS][$id])?$container->_rf[self::RF_NAMED_CONTROLS][$id]:null;
+ else
+ {
+ $cid=substr($id,0,$pos);
+ $sid=substr($id,$pos+1);
+ if(isset($container->_rf[self::RF_NAMED_CONTROLS][$cid]))
+ return $container->_rf[self::RF_NAMED_CONTROLS][$cid]->findControl($sid);
+ else
+ return null;
+ }
+ }
+ public function findControlsByType($type,$strict=true)
+ {
+ $controls=array();
+ if($this->getHasControls())
+ {
+ foreach($this->_rf[self::RF_CONTROLS] as $control)
+ {
+ if(is_object($control) && (get_class($control)===$type || (!$strict && ($control instanceof $type))))
+ $controls[]=$control;
+ if(($control instanceof TControl) && $control->getHasControls())
+ $controls=array_merge($controls,$control->findControlsByType($type,$strict));
+ }
+ }
+ return $controls;
+ }
+ public function findControlsByID($id)
+ {
+ $controls=array();
+ if($this->getHasControls())
+ {
+ foreach($this->_rf[self::RF_CONTROLS] as $control)
+ {
+ if($control instanceof TControl)
+ {
+ if($control->_id===$id)
+ $controls[]=$control;
+ $controls=array_merge($controls,$control->findControlsByID($id));
+ }
+ }
+ }
+ return $controls;
+ }
+ public function clearNamingContainer()
+ {
+ unset($this->_rf[self::RF_NAMED_CONTROLS_ID]);
+ $this->clearNameTable();
+ }
+ public function registerObject($name,$object)
+ {
+ if(isset($this->_rf[self::RF_NAMED_OBJECTS][$name]))
+ throw new TInvalidOperationException('control_object_reregistered',$name);
+ $this->_rf[self::RF_NAMED_OBJECTS][$name]=$object;
+ }
+ public function unregisterObject($name)
+ {
+ unset($this->_rf[self::RF_NAMED_OBJECTS][$name]);
+ }
+ public function isObjectRegistered($name)
+ {
+ return isset($this->_rf[self::RF_NAMED_OBJECTS][$name]);
+ }
+ public function getHasChildInitialized()
+ {
+ return $this->getControlStage() >= self::CS_CHILD_INITIALIZED;
+ }
+ public function getHasInitialized()
+ {
+ return $this->getControlStage() >= self::CS_INITIALIZED;
+ }
+ public function getHasLoadedPostData()
+ {
+ return $this->getControlStage() >= self::CS_STATE_LOADED;
+ }
+ public function getHasLoaded()
+ {
+ return $this->getControlStage() >= self::CS_LOADED;
+ }
+ public function getHasPreRendered()
+ {
+ return $this->getControlStage() >= self::CS_PRERENDERED;
+ }
+ public function getRegisteredObject($name)
+ {
+ return isset($this->_rf[self::RF_NAMED_OBJECTS][$name])?$this->_rf[self::RF_NAMED_OBJECTS][$name]:null;
+ }
+ public function getAllowChildControls()
+ {
+ return true;
+ }
+ public function addParsedObject($object)
+ {
+ $this->getControls()->add($object);
+ }
+ final protected function clearChildState()
+ {
+ unset($this->_rf[self::RF_CHILD_STATE]);
+ }
+ final protected function isDescendentOf($ancestor)
+ {
+ $control=$this;
+ while($control!==$ancestor && $control->_parent)
+ $control=$control->_parent;
+ return $control===$ancestor;
+ }
+ public function addedControl($control)
+ {
+ if($control->_parent)
+ $control->_parent->getControls()->remove($control);
+ $control->_parent=$this;
+ $control->_page=$this->getPage();
+ $namingContainer=($this instanceof INamingContainer)?$this:$this->_namingContainer;
+ if($namingContainer)
+ {
+ $control->_namingContainer=$namingContainer;
+ if($control->_id==='')
+ $control->generateAutomaticID();
+ else
+ $namingContainer->clearNameTable();
+ $control->clearCachedUniqueID($control instanceof INamingContainer);
+ }
+ if($this->_stage>=self::CS_CHILD_INITIALIZED)
+ {
+ $control->initRecursive($namingContainer);
+ if($this->_stage>=self::CS_STATE_LOADED)
+ {
+ if(isset($this->_rf[self::RF_CHILD_STATE][$control->_id]))
+ {
+ $state=$this->_rf[self::RF_CHILD_STATE][$control->_id];
+ unset($this->_rf[self::RF_CHILD_STATE][$control->_id]);
+ }
+ else
+ $state=null;
+ $control->loadStateRecursive($state,!($this->_flags & self::IS_DISABLE_VIEWSTATE));
+ if($this->_stage>=self::CS_LOADED)
+ {
+ $control->loadRecursive();
+ if($this->_stage>=self::CS_PRERENDERED)
+ $control->preRenderRecursive();
+ }
+ }
+ }
+ }
+ public function removedControl($control)
+ {
+ if($this->_namingContainer)
+ $this->_namingContainer->clearNameTable();
+ $control->unloadRecursive();
+ $control->_parent=null;
+ $control->_page=null;
+ $control->_namingContainer=null;
+ $control->_tplControl=null;
+ if(!($control->_flags & self::IS_ID_SET))
+ $control->_id='';
+ else
+ unset($this->_rf[self::RF_NAMED_OBJECTS][$control->_id]);
+ $control->clearCachedUniqueID(true);
+ }
+ protected function initRecursive($namingContainer=null)
+ {
+ $this->ensureChildControls();
+ if($this->getHasControls())
+ {
+ if($this instanceof INamingContainer)
+ $namingContainer=$this;
+ $page=$this->getPage();
+ foreach($this->_rf[self::RF_CONTROLS] as $control)
+ {
+ if($control instanceof TControl)
+ {
+ $control->_namingContainer=$namingContainer;
+ $control->_page=$page;
+ if($control->_id==='' && $namingContainer)
+ $control->generateAutomaticID();
+ $control->initRecursive($namingContainer);
+ }
+ }
+ }
+ if($this->_stage<self::CS_INITIALIZED)
+ {
+ $this->_stage=self::CS_CHILD_INITIALIZED;
+ if(($page=$this->getPage()) && $this->getEnableTheming() && !($this->_flags & self::IS_SKIN_APPLIED))
+ {
+ $page->applyControlSkin($this);
+ $this->_flags |= self::IS_SKIN_APPLIED;
+ }
+ if(isset($this->_rf[self::RF_ADAPTER]))
+ $this->_rf[self::RF_ADAPTER]->onInit(null);
+ else
+ $this->onInit(null);
+ $this->_stage=self::CS_INITIALIZED;
+ }
+ }
+ protected function loadRecursive()
+ {
+ if($this->_stage<self::CS_LOADED)
+ {
+ if(isset($this->_rf[self::RF_ADAPTER]))
+ $this->_rf[self::RF_ADAPTER]->onLoad(null);
+ else
+ $this->onLoad(null);
+ }
+ if($this->getHasControls())
+ {
+ foreach($this->_rf[self::RF_CONTROLS] as $control)
+ {
+ if($control instanceof TControl)
+ $control->loadRecursive();
+ }
+ }
+ if($this->_stage<self::CS_LOADED)
+ $this->_stage=self::CS_LOADED;
+ }
+ protected function preRenderRecursive()
+ {
+ $this->autoDataBindProperties();
+ if($this->getVisible(false))
+ {
+ if(isset($this->_rf[self::RF_ADAPTER]))
+ $this->_rf[self::RF_ADAPTER]->onPreRender(null);
+ else
+ $this->onPreRender(null);
+ if($this->getHasControls())
+ {
+ foreach($this->_rf[self::RF_CONTROLS] as $control)
+ {
+ if($control instanceof TControl)
+ $control->preRenderRecursive();
+ else if($control instanceof TCompositeLiteral)
+ $control->evaluateDynamicContent();
+ }
+ }
+ $this->addToPostDataLoader();
+ }
+ $this->_stage=self::CS_PRERENDERED;
+ }
+ protected function addToPostDataLoader()
+ {
+ if($this instanceof IPostBackDataHandler)
+ $this->getPage()->registerPostDataLoader($this);
+ }
+ protected function unloadRecursive()
+ {
+ if(!($this->_flags & self::IS_ID_SET))
+ $this->_id='';
+ if($this->getHasControls())
+ {
+ foreach($this->_rf[self::RF_CONTROLS] as $control)
+ if($control instanceof TControl)
+ $control->unloadRecursive();
+ }
+ if(isset($this->_rf[self::RF_ADAPTER]))
+ $this->_rf[self::RF_ADAPTER]->onUnload(null);
+ else
+ $this->onUnload(null);
+ }
+ public function onInit($param)
+ {
+ $this->raiseEvent('OnInit',$this,$param);
+ }
+ public function onLoad($param)
+ {
+ $this->raiseEvent('OnLoad',$this,$param);
+ }
+ public function onDataBinding($param)
+ {
+ $this->raiseEvent('OnDataBinding',$this,$param);
+ }
+ public function onUnload($param)
+ {
+ $this->raiseEvent('OnUnload',$this,$param);
+ }
+ public function onPreRender($param)
+ {
+ $this->raiseEvent('OnPreRender',$this,$param);
+ }
+ protected function raiseBubbleEvent($sender,$param)
+ {
+ $control=$this;
+ while($control=$control->_parent)
+ {
+ if($control->bubbleEvent($sender,$param))
+ break;
+ }
+ }
+ public function bubbleEvent($sender,$param)
+ {
+ return false;
+ }
+ public function broadcastEvent($name,$sender,$param)
+ {
+ $rootControl=(($page=$this->getPage())===null)?$this:$page;
+ $rootControl->broadcastEventInternal($name,$sender,new TBroadcastEventParameter($name,$param));
+ }
+ private function broadcastEventInternal($name,$sender,$param)
+ {
+ if($this->hasEvent($name))
+ $this->raiseEvent($name,$sender,$param->getParameter());
+ if($this instanceof IBroadcastEventReceiver)
+ $this->broadcastEventReceived($sender,$param);
+ if($this->getHasControls())
+ {
+ foreach($this->_rf[self::RF_CONTROLS] as $control)
+ {
+ if($control instanceof TControl)
+ $control->broadcastEventInternal($name,$sender,$param);
+ }
+ }
+ }
+ protected function traverseChildControls($param,$preCallback=null,$postCallback=null)
+ {
+ if($preCallback!==null)
+ call_user_func($preCallback,$this,$param);
+ if($this->getHasControls())
+ {
+ foreach($this->_rf[self::RF_CONTROLS] as $control)
+ {
+ if($control instanceof TControl)
+ {
+ $control->traverseChildControls($param,$preCallback,$postCallback);
+ }
+ }
+ }
+ if($postCallback!==null)
+ call_user_func($postCallback,$this,$param);
+ }
+ public function renderControl($writer)
+ {
+ if($this->getVisible(false))
+ {
+ if(isset($this->_rf[self::RF_ADAPTER]))
+ $this->_rf[self::RF_ADAPTER]->render($writer);
+ else
+ $this->render($writer);
+ }
+ }
+ public function render($writer)
+ {
+ $this->renderChildren($writer);
+ }
+ public function renderChildren($writer)
+ {
+ if($this->getHasControls())
+ {
+ foreach($this->_rf[self::RF_CONTROLS] as $control)
+ {
+ if(is_string($control))
+ $writer->write($control);
+ else if($control instanceof TControl)
+ $control->renderControl($writer);
+ else if($control instanceof IRenderable)
+ $control->render($writer);
+ }
+ }
+ }
+ public function saveState()
+ {
+ }
+ public function loadState()
+ {
+ }
+ protected function loadStateRecursive(&$state,$needViewState=true)
+ {
+ if(is_array($state))
+ {
+ $needViewState=($needViewState && !($this->_flags & self::IS_DISABLE_VIEWSTATE));
+ if(isset($state[1]))
+ {
+ $this->_rf[self::RF_CONTROLSTATE]=&$state[1];
+ unset($state[1]);
+ }
+ else
+ unset($this->_rf[self::RF_CONTROLSTATE]);
+ if($needViewState)
+ {
+ if(isset($state[0]))
+ $this->_viewState=&$state[0];
+ else
+ $this->_viewState=array();
+ }
+ unset($state[0]);
+ if($this->getHasControls())
+ {
+ foreach($this->_rf[self::RF_CONTROLS] as $control)
+ {
+ if($control instanceof TControl)
+ {
+ if(isset($state[$control->_id]))
+ {
+ $control->loadStateRecursive($state[$control->_id],$needViewState);
+ unset($state[$control->_id]);
+ }
+ }
+ }
+ }
+ if(!empty($state))
+ $this->_rf[self::RF_CHILD_STATE]=&$state;
+ }
+ $this->_stage=self::CS_STATE_LOADED;
+ if(isset($this->_rf[self::RF_ADAPTER]))
+ $this->_rf[self::RF_ADAPTER]->loadState();
+ else
+ $this->loadState();
+ }
+ protected function &saveStateRecursive($needViewState=true)
+ {
+ if(isset($this->_rf[self::RF_ADAPTER]))
+ $this->_rf[self::RF_ADAPTER]->saveState();
+ else
+ $this->saveState();
+ $needViewState=($needViewState && !($this->_flags & self::IS_DISABLE_VIEWSTATE));
+ $state=array();
+ if($this->getHasControls())
+ {
+ foreach($this->_rf[self::RF_CONTROLS] as $control)
+ {
+ if($control instanceof TControl)
+ $state[$control->_id]=&$control->saveStateRecursive($needViewState);
+ }
+ }
+ if($needViewState && !empty($this->_viewState))
+ $state[0]=&$this->_viewState;
+ if(isset($this->_rf[self::RF_CONTROLSTATE]))
+ $state[1]=&$this->_rf[self::RF_CONTROLSTATE];
+ return $state;
+ }
+ public function applyStyleSheetSkin($page)
+ {
+ if($page && !($this->_flags & self::IS_STYLESHEET_APPLIED))
+ {
+ $page->applyControlStyleSheet($this);
+ $this->_flags |= self::IS_STYLESHEET_APPLIED;
+ }
+ else if($this->_flags & self::IS_STYLESHEET_APPLIED)
+ throw new TInvalidOperationException('control_stylesheet_applied',get_class($this));
+ }
+ private function clearCachedUniqueID($recursive)
+ {
+ if($recursive && $this->_uid!==null && isset($this->_rf[self::RF_CONTROLS]))
+ {
+ foreach($this->_rf[self::RF_CONTROLS] as $control)
+ if($control instanceof TControl)
+ $control->clearCachedUniqueID($recursive);
+ }
+ $this->_uid=null;
+ }
+ private function generateAutomaticID()
+ {
+ $this->_flags &= ~self::IS_ID_SET;
+ if(!isset($this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID]))
+ $this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID]=0;
+ $id=$this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID]++;
+ $this->_id=self::AUTOMATIC_ID_PREFIX . $id;
+ $this->_namingContainer->clearNameTable();
+ }
+ private function clearNameTable()
+ {
+ unset($this->_rf[self::RF_NAMED_CONTROLS]);
+ }
+ private function fillNameTable($container,$controls)
+ {
+ foreach($controls as $control)
+ {
+ if($control instanceof TControl)
+ {
+ if($control->_id!=='')
+ {
+ if(isset($container->_rf[self::RF_NAMED_CONTROLS][$control->_id]))
+ throw new TInvalidDataValueException('control_id_nonunique',get_class($control),$control->_id);
+ else
+ $container->_rf[self::RF_NAMED_CONTROLS][$control->_id]=$control;
+ }
+ if(!($control instanceof INamingContainer) && $control->getHasControls())
+ $this->fillNameTable($container,$control->_rf[self::RF_CONTROLS]);
+ }
+ }
+ }
+}
+class TControlCollection extends TList
+{
+ private $_o;
+ public function __construct(TControl $owner,$readOnly=false)
+ {
+ $this->_o=$owner;
+ parent::__construct(null,$readOnly);
+ }
+ protected function getOwner()
+ {
+ return $this->_o;
+ }
+ public function insertAt($index,$item)
+ {
+ if($item instanceof TControl)
+ {
+ parent::insertAt($index,$item);
+ $this->_o->addedControl($item);
+ }
+ else if(is_string($item) || ($item instanceof IRenderable))
+ parent::insertAt($index,$item);
+ else
+ throw new TInvalidDataTypeException('controlcollection_control_required');
+ }
+ public function removeAt($index)
+ {
+ $item=parent::removeAt($index);
+ if($item instanceof TControl)
+ $this->_o->removedControl($item);
+ return $item;
+ }
+ public function clear()
+ {
+ parent::clear();
+ if($this->_o instanceof INamingContainer)
+ $this->_o->clearNamingContainer();
+ }
+}
+class TEmptyControlCollection extends TControlCollection
+{
+ public function __construct(TControl $owner)
+ {
+ parent::__construct($owner,true);
+ }
+ public function insertAt($index,$item)
+ {
+ if(!is_string($item))
+ parent::insertAt($index,$item);
+ }
+}
+interface INamingContainer
+{
+}
+interface IPostBackEventHandler
+{
+ public function raisePostBackEvent($param);
+}
+interface IPostBackDataHandler
+{
+ public function loadPostData($key,$values);
+ public function raisePostDataChangedEvent();
+ public function getDataChanged();
+}
+interface IValidator
+{
+ public function validate();
+ public function getIsValid();
+ public function setIsValid($value);
+ public function getErrorMessage();
+ public function setErrorMessage($value);
+}
+interface IValidatable
+{
+ public function getValidationPropertyValue();
+ public function getIsValid();
+ public function setIsValid($value);
+}
+interface IBroadcastEventReceiver
+{
+ public function broadcastEventReceived($sender,$param);
+}
+interface ITheme
+{
+ public function applySkin($control);
+}
+interface ITemplate
+{
+ public function instantiateIn($parent);
+}
+interface IButtonControl
+{
+ public function getText();
+ public function setText($value);
+ public function getCausesValidation();
+ public function setCausesValidation($value);
+ public function getCommandName();
+ public function setCommandName($value);
+ public function getCommandParameter();
+ public function setCommandParameter($value);
+ public function getValidationGroup();
+ public function setValidationGroup($value);
+ public function onClick($param);
+ public function onCommand($param);
+ public function setIsDefaultButton($value);
+ public function getIsDefaultButton();
+}
+interface ISurroundable
+{
+ public function getSurroundingTagID();
+}
+class TBroadcastEventParameter extends TEventParameter
+{
+ private $_name;
+ private $_param;
+ public function __construct($name='',$parameter=null)
+ {
+ $this->_name=$name;
+ $this->_param=$parameter;
+ }
+ public function getName()
+ {
+ return $this->_name;
+ }
+ public function setName($value)
+ {
+ $this->_name=$value;
+ }
+ public function getParameter()
+ {
+ return $this->_param;
+ }
+ public function setParameter($value)
+ {
+ $this->_param=$value;
+ }
+}
+class TCommandEventParameter extends TEventParameter
+{
+ private $_name;
+ private $_param;
+ public function __construct($name='',$parameter='')
+ {
+ $this->_name=$name;
+ $this->_param=$parameter;
+ }
+ public function getCommandName()
+ {
+ return $this->_name;
+ }
+ public function getCommandParameter()
+ {
+ return $this->_param;
+ }
+}
+class TCompositeLiteral extends TComponent implements IRenderable, IBindable
+{
+ const TYPE_EXPRESSION=0;
+ const TYPE_STATEMENTS=1;
+ const TYPE_DATABINDING=2;
+ private $_container=null;
+ private $_items=array();
+ private $_expressions=array();
+ private $_statements=array();
+ private $_bindings=array();
+ public function __construct($items)
+ {
+ $this->_items=array();
+ $this->_expressions=array();
+ $this->_statements=array();
+ foreach($items as $id=>$item)
+ {
+ if(is_array($item))
+ {
+ if($item[0]===self::TYPE_EXPRESSION)
+ $this->_expressions[$id]=$item[1];
+ else if($item[0]===self::TYPE_STATEMENTS)
+ $this->_statements[$id]=$item[1];
+ else if($item[0]===self::TYPE_DATABINDING)
+ $this->_bindings[$id]=$item[1];
+ $this->_items[$id]='';
+ }
+ else
+ $this->_items[$id]=$item;
+ }
+ }
+ public function getContainer()
+ {
+ return $this->_container;
+ }
+ public function setContainer(TComponent $value)
+ {
+ $this->_container=$value;
+ }
+ public function evaluateDynamicContent()
+ {
+ $context=$this->_container===null?$this:$this->_container;
+ foreach($this->_expressions as $id=>$expression)
+ $this->_items[$id]=$context->evaluateExpression($expression);
+ foreach($this->_statements as $id=>$statement)
+ $this->_items[$id]=$context->evaluateStatements($statement);
+ }
+ public function dataBind()
+ {
+ $context=$this->_container===null?$this:$this->_container;
+ foreach($this->_bindings as $id=>$binding)
+ $this->_items[$id]=$context->evaluateExpression($binding);
+ }
+ public function render($writer)
+ {
+ $writer->write(implode('',$this->_items));
+ }
+}
+class TFont extends TComponent
+{
+ const IS_BOLD=0x01;
+ const IS_ITALIC=0x02;
+ const IS_OVERLINE=0x04;
+ const IS_STRIKEOUT=0x08;
+ const IS_UNDERLINE=0x10;
+ const IS_SET_BOLD=0x01000;
+ const IS_SET_ITALIC=0x02000;
+ const IS_SET_OVERLINE=0x04000;
+ const IS_SET_STRIKEOUT=0x08000;
+ const IS_SET_UNDERLINE=0x10000;
+ const IS_SET_SIZE=0x20000;
+ const IS_SET_NAME=0x40000;
+ private $_flags=0;
+ private $_name='';
+ private $_size='';
+ public function getBold()
+ {
+ return ($this->_flags & self::IS_BOLD)!==0;
+ }
+ public function setBold($value)
+ {
+ $this->_flags |= self::IS_SET_BOLD;
+ if(TPropertyValue::ensureBoolean($value))
+ $this->_flags |= self::IS_BOLD;
+ else
+ $this->_flags &= ~self::IS_BOLD;
+ }
+ public function getItalic()
+ {
+ return ($this->_flags & self::IS_ITALIC)!==0;
+ }
+ public function setItalic($value)
+ {
+ $this->_flags |= self::IS_SET_ITALIC;
+ if(TPropertyValue::ensureBoolean($value))
+ $this->_flags |= self::IS_ITALIC;
+ else
+ $this->_flags &= ~self::IS_ITALIC;
+ }
+ public function getOverline()
+ {
+ return ($this->_flags & self::IS_OVERLINE)!==0;
+ }
+ public function setOverline($value)
+ {
+ $this->_flags |= self::IS_SET_OVERLINE;
+ if(TPropertyValue::ensureBoolean($value))
+ $this->_flags |= self::IS_OVERLINE;
+ else
+ $this->_flags &= ~self::IS_OVERLINE;
+ }
+ public function getSize()
+ {
+ return $this->_size;
+ }
+ public function setSize($value)
+ {
+ $this->_flags |= self::IS_SET_SIZE;
+ $this->_size=$value;
+ }
+ public function getStrikeout()
+ {
+ return ($this->_flags & self::IS_STRIKEOUT)!==0;
+ }
+ public function setStrikeout($value)
+ {
+ $this->_flags |= self::IS_SET_STRIKEOUT;
+ if(TPropertyValue::ensureBoolean($value))
+ $this->_flags |= self::IS_STRIKEOUT;
+ else
+ $this->_flags &= ~self::IS_STRIKEOUT;
+ }
+ public function getUnderline()
+ {
+ return ($this->_flags & self::IS_UNDERLINE)!==0;
+ }
+ public function setUnderline($value)
+ {
+ $this->_flags |= self::IS_SET_UNDERLINE;
+ if(TPropertyValue::ensureBoolean($value))
+ $this->_flags |= self::IS_UNDERLINE;
+ else
+ $this->_flags &= ~self::IS_UNDERLINE;
+ }
+ public function getName()
+ {
+ return $this->_name;
+ }
+ public function setName($value)
+ {
+ $this->_flags |= self::IS_SET_NAME;
+ $this->_name=$value;
+ }
+ public function getIsEmpty()
+ {
+ return !$this->_flags;
+ }
+ public function reset()
+ {
+ $this->_flags=0;
+ $this->_name='';
+ $this->_size='';
+ }
+ public function mergeWith($font)
+ {
+ if($font===null || $font->_flags===0)
+ return;
+ if(!($this->_flags & self::IS_SET_BOLD) && ($font->_flags & self::IS_SET_BOLD))
+ $this->setBold($font->getBold());
+ if(!($this->_flags & self::IS_SET_ITALIC) && ($font->_flags & self::IS_SET_ITALIC))
+ $this->setItalic($font->getItalic());
+ if(!($this->_flags & self::IS_SET_OVERLINE) && ($font->_flags & self::IS_SET_OVERLINE))
+ $this->setOverline($font->getOverline());
+ if(!($this->_flags & self::IS_SET_STRIKEOUT) && ($font->_flags & self::IS_SET_STRIKEOUT))
+ $this->setStrikeout($font->getStrikeout());
+ if(!($this->_flags & self::IS_SET_UNDERLINE) && ($font->_flags & self::IS_SET_UNDERLINE))
+ $this->setUnderline($font->getUnderline());
+ if(!($this->_flags & self::IS_SET_SIZE) && ($font->_flags & self::IS_SET_SIZE))
+ $this->setSize($font->getSize());
+ if(!($this->_flags & self::IS_SET_NAME) && ($font->_flags & self::IS_SET_NAME))
+ $this->setName($font->getName());
+ }
+ public function copyFrom($font)
+ {
+ if($font===null || $font->_flags===0)
+ return;
+ if($font->_flags & self::IS_SET_BOLD)
+ $this->setBold($font->getBold());
+ if($font->_flags & self::IS_SET_ITALIC)
+ $this->setItalic($font->getItalic());
+ if($font->_flags & self::IS_SET_OVERLINE)
+ $this->setOverline($font->getOverline());
+ if($font->_flags & self::IS_SET_STRIKEOUT)
+ $this->setStrikeout($font->getStrikeout());
+ if($font->_flags & self::IS_SET_UNDERLINE)
+ $this->setUnderline($font->getUnderline());
+ if($font->_flags & self::IS_SET_SIZE)
+ $this->setSize($font->getSize());
+ if($font->_flags & self::IS_SET_NAME)
+ $this->setName($font->getName());
+ }
+ public function toString()
+ {
+ if($this->_flags===0)
+ return '';
+ $str='';
+ if($this->_flags & self::IS_SET_BOLD)
+ $str.='font-weight:'.(($this->_flags & self::IS_BOLD)?'bold;':'normal;');
+ if($this->_flags & self::IS_SET_ITALIC)
+ $str.='font-style:'.(($this->_flags & self::IS_ITALIC)?'italic;':'normal;');
+ $textDec='';
+ if($this->_flags & self::IS_UNDERLINE)
+ $textDec.='underline';
+ if($this->_flags & self::IS_OVERLINE)
+ $textDec.=' overline';
+ if($this->_flags & self::IS_STRIKEOUT)
+ $textDec.=' line-through';
+ $textDec=ltrim($textDec);
+ if($textDec!=='')
+ $str.='text-decoration:'.$textDec.';';
+ if($this->_size!=='')
+ $str.='font-size:'.$this->_size.';';
+ if($this->_name!=='')
+ $str.='font-family:'.$this->_name.';';
+ return $str;
+ }
+ public function addAttributesToRender($writer)
+ {
+ if($this->_flags===0)
+ return;
+ if($this->_flags & self::IS_SET_BOLD)
+ $writer->addStyleAttribute('font-weight',(($this->_flags & self::IS_BOLD)?'bold':'normal'));
+ if($this->_flags & self::IS_SET_ITALIC)
+ $writer->addStyleAttribute('font-style',(($this->_flags & self::IS_ITALIC)?'italic':'normal'));
+ $textDec='';
+ if($this->_flags & self::IS_UNDERLINE)
+ $textDec.='underline';
+ if($this->_flags & self::IS_OVERLINE)
+ $textDec.=' overline';
+ if($this->_flags & self::IS_STRIKEOUT)
+ $textDec.=' line-through';
+ $textDec=ltrim($textDec);
+ if($textDec!=='')
+ $writer->addStyleAttribute('text-decoration',$textDec);
+ if($this->_size!=='')
+ $writer->addStyleAttribute('font-size',$this->_size);
+ if($this->_name!=='')
+ $writer->addStyleAttribute('font-family',$this->_name);
+ }
+}
+class TStyle extends TComponent
+{
+ private $_fields=array();
+ private $_font=null;
+ private $_class=null;
+ private $_customStyle=null;
+ private $_displayStyle='Fixed';
+ public function __construct($style=null)
+ {
+ if($style!==null)
+ $this->copyFrom($style);
+ }
+ public function __clone()
+ {
+ if($this->_font!==null)
+ $this->_font = clone($this->_font);
+ }
+ public function getBackColor()
+ {
+ return isset($this->_fields['background-color'])?$this->_fields['background-color']:'';
+ }
+ public function setBackColor($value)
+ {
+ if(trim($value)==='')
+ unset($this->_fields['background-color']);
+ else
+ $this->_fields['background-color']=$value;
+ }
+ public function getBorderColor()
+ {
+ return isset($this->_fields['border-color'])?$this->_fields['border-color']:'';
+ }
+ public function setBorderColor($value)
+ {
+ if(trim($value)==='')
+ unset($this->_fields['border-color']);
+ else
+ $this->_fields['border-color']=$value;
+ }
+ public function getBorderStyle()
+ {
+ return isset($this->_fields['border-style'])?$this->_fields['border-style']:'';
+ }
+ public function setBorderStyle($value)
+ {
+ if(trim($value)==='')
+ unset($this->_fields['border-style']);
+ else
+ $this->_fields['border-style']=$value;
+ }
+ public function getBorderWidth()
+ {
+ return isset($this->_fields['border-width'])?$this->_fields['border-width']:'';
+ }
+ public function setBorderWidth($value)
+ {
+ if(trim($value)==='')
+ unset($this->_fields['border-width']);
+ else
+ $this->_fields['border-width']=$value;
+ }
+ public function getCssClass()
+ {
+ return $this->_class===null?'':$this->_class;
+ }
+ public function hasCssClass()
+ {
+ return ($this->_class!==null);
+ }
+ public function setCssClass($value)
+ {
+ $this->_class=$value;
+ }
+ public function getFont()
+ {
+ if($this->_font===null)
+ $this->_font=new TFont;
+ return $this->_font;
+ }
+ public function hasFont()
+ {
+ return $this->_font !== null;
+ }
+ public function setDisplayStyle($value)
+ {
+ $this->_displayStyle = TPropertyValue::ensureEnum($value, 'TDisplayStyle');
+ switch($this->_displayStyle)
+ {
+ case TDisplayStyle::None:
+ $this->_fields['display'] = 'none';
+ break;
+ case TDisplayStyle::Dynamic:
+ $this->_fields['display'] = '';
+ break;
+ case TDisplayStyle::Fixed:
+ $this->_fields['visibility'] = 'visible';
+ break;
+ case TDisplayStyle::Hidden:
+ $this->_fields['visibility'] = 'hidden';
+ break;
+ }
+ }
+ public function getDisplayStyle()
+ {
+ return $this->_displayStyle;
+ }
+ public function getForeColor()
+ {
+ return isset($this->_fields['color'])?$this->_fields['color']:'';
+ }
+ public function setForeColor($value)
+ {
+ if(trim($value)==='')
+ unset($this->_fields['color']);
+ else
+ $this->_fields['color']=$value;
+ }
+ public function getHeight()
+ {
+ return isset($this->_fields['height'])?$this->_fields['height']:'';
+ }
+ public function setHeight($value)
+ {
+ if(trim($value)==='')
+ unset($this->_fields['height']);
+ else
+ $this->_fields['height']=$value;
+ }
+ public function getCustomStyle()
+ {
+ return $this->_customStyle===null?'':$this->_customStyle;
+ }
+ public function setCustomStyle($value)
+ {
+ $this->_customStyle=$value;
+ }
+ public function getStyleField($name)
+ {
+ return isset($this->_fields[$name])?$this->_fields[$name]:'';
+ }
+ public function setStyleField($name,$value)
+ {
+ $this->_fields[$name]=$value;
+ }
+ public function clearStyleField($name)
+ {
+ unset($this->_fields[$name]);
+ }
+ public function hasStyleField($name)
+ {
+ return isset($this->_fields[$name]);
+ }
+ public function getWidth()
+ {
+ return isset($this->_fields['width'])?$this->_fields['width']:'';
+ }
+ public function setWidth($value)
+ {
+ $this->_fields['width']=$value;
+ }
+ public function reset()
+ {
+ $this->_fields=array();
+ $this->_font=null;
+ $this->_class=null;
+ $this->_customStyle=null;
+ }
+ public function copyFrom($style)
+ {
+ if($style instanceof TStyle)
+ {
+ $this->_fields=array_merge($this->_fields,$style->_fields);
+ if($style->_class!==null)
+ $this->_class=$style->_class;
+ if($style->_customStyle!==null)
+ $this->_customStyle=$style->_customStyle;
+ if($style->_font!==null)
+ $this->getFont()->copyFrom($style->_font);
+ }
+ }
+ public function mergeWith($style)
+ {
+ if($style instanceof TStyle)
+ {
+ $this->_fields=array_merge($style->_fields,$this->_fields);
+ if($this->_class===null)
+ $this->_class=$style->_class;
+ if($this->_customStyle===null)
+ $this->_customStyle=$style->_customStyle;
+ if($style->_font!==null)
+ $this->getFont()->mergeWith($style->_font);
+ }
+ }
+ public function addAttributesToRender($writer)
+ {
+ if($this->_customStyle!==null)
+ {
+ foreach(explode(';',$this->_customStyle) as $style)
+ {
+ $arr=explode(':',$style);
+ if(isset($arr[1]) && trim($arr[0])!=='')
+ $writer->addStyleAttribute(trim($arr[0]),trim($arr[1]));
+ }
+ }
+ $writer->addStyleAttributes($this->_fields);
+ if($this->_font!==null)
+ $this->_font->addAttributesToRender($writer);
+ if($this->_class!==null)
+ $writer->addAttribute('class',$this->_class);
+ }
+ public function getStyleFields()
+ {
+ return $this->_fields;
+ }
+}
+class TDisplayStyle extends TEnumerable
+{
+ const None='None';
+ const Dynamic='Dynamic';
+ const Fixed='Fixed';
+ const Hidden='Hidden';
+}
+class TTableStyle extends TStyle
+{
+ private $_backImageUrl=null;
+ private $_horizontalAlign=null;
+ private $_cellPadding=null;
+ private $_cellSpacing=null;
+ private $_gridLines=null;
+ private $_borderCollapse=null;
+ public function reset()
+ {
+ $this->_backImageUrl=null;
+ $this->_horizontalAlign=null;
+ $this->_cellPadding=null;
+ $this->_cellSpacing=null;
+ $this->_gridLines=null;
+ $this->_borderCollapse=null;
+ }
+ public function copyFrom($style)
+ {
+ parent::copyFrom($style);
+ if($style instanceof TTableStyle)
+ {
+ if($style->_backImageUrl!==null)
+ $this->_backImageUrl=$style->_backImageUrl;
+ if($style->_horizontalAlign!==null)
+ $this->_horizontalAlign=$style->_horizontalAlign;
+ if($style->_cellPadding!==null)
+ $this->_cellPadding=$style->_cellPadding;
+ if($style->_cellSpacing!==null)
+ $this->_cellSpacing=$style->_cellSpacing;
+ if($style->_gridLines!==null)
+ $this->_gridLines=$style->_gridLines;
+ if($style->_borderCollapse!==null)
+ $this->_borderCollapse=$style->_borderCollapse;
+ }
+ }
+ public function mergeWith($style)
+ {
+ parent::mergeWith($style);
+ if($style instanceof TTableStyle)
+ {
+ if($this->_backImageUrl===null && $style->_backImageUrl!==null)
+ $this->_backImageUrl=$style->_backImageUrl;
+ if($this->_horizontalAlign===null && $style->_horizontalAlign!==null)
+ $this->_horizontalAlign=$style->_horizontalAlign;
+ if($this->_cellPadding===null && $style->_cellPadding!==null)
+ $this->_cellPadding=$style->_cellPadding;
+ if($this->_cellSpacing===null && $style->_cellSpacing!==null)
+ $this->_cellSpacing=$style->_cellSpacing;
+ if($this->_gridLines===null && $style->_gridLines!==null)
+ $this->_gridLines=$style->_gridLines;
+ if($this->_borderCollapse===null && $style->_borderCollapse!==null)
+ $this->_borderCollapse=$style->_borderCollapse;
+ }
+ }
+ public function addAttributesToRender($writer)
+ {
+ if(($url=trim($this->getBackImageUrl()))!=='')
+ $writer->addStyleAttribute('background-image','url('.$url.')');
+ if(($horizontalAlign=$this->getHorizontalAlign())!==THorizontalAlign::NotSet)
+ $writer->addStyleAttribute('text-align',strtolower($horizontalAlign));
+ if(($cellPadding=$this->getCellPadding())>=0)
+ $writer->addAttribute('cellpadding',"$cellPadding");
+ if(($cellSpacing=$this->getCellSpacing())>=0)
+ $writer->addAttribute('cellspacing',"$cellSpacing");
+ if($this->getBorderCollapse())
+ $writer->addStyleAttribute('border-collapse','collapse');
+ switch($this->getGridLines())
+ {
+ case TTableGridLines::Horizontal : $writer->addAttribute('rules','rows'); break;
+ case TTableGridLines::Vertical : $writer->addAttribute('rules','cols'); break;
+ case TTableGridLines::Both : $writer->addAttribute('rules','all'); break;
+ }
+ parent::addAttributesToRender($writer);
+ }
+ public function getBackImageUrl()
+ {
+ return $this->_backImageUrl===null?'':$this->_backImageUrl;
+ }
+ public function setBackImageUrl($value)
+ {
+ $this->_backImageUrl=$value;
+ }
+ public function getHorizontalAlign()
+ {
+ return $this->_horizontalAlign===null?THorizontalAlign::NotSet:$this->_horizontalAlign;
+ }
+ public function setHorizontalAlign($value)
+ {
+ $this->_horizontalAlign=TPropertyValue::ensureEnum($value,'THorizontalAlign');
+ }
+ public function getCellPadding()
+ {
+ return $this->_cellPadding===null?-1:$this->_cellPadding;
+ }
+ public function setCellPadding($value)
+ {
+ if(($this->_cellPadding=TPropertyValue::ensureInteger($value))<-1)
+ throw new TInvalidDataValueException('tablestyle_cellpadding_invalid');
+ }
+ public function getCellSpacing()
+ {
+ return $this->_cellSpacing===null?-1:$this->_cellSpacing;
+ }
+ public function setCellSpacing($value)
+ {
+ if(($this->_cellSpacing=TPropertyValue::ensureInteger($value))<-1)
+ throw new TInvalidDataValueException('tablestyle_cellspacing_invalid');
+ }
+ public function getGridLines()
+ {
+ return $this->_gridLines===null?TTableGridLines::None:$this->_gridLines;
+ }
+ public function setGridLines($value)
+ {
+ $this->_gridLines=TPropertyValue::ensureEnum($value,'TTableGridLines');
+ }
+ public function getBorderCollapse()
+ {
+ return $this->_borderCollapse===null?false:$this->_borderCollapse;
+ }
+ public function setBorderCollapse($value)
+ {
+ $this->_borderCollapse=TPropertyValue::ensureBoolean($value);
+ }
+}
+class TTableItemStyle extends TStyle
+{
+ private $_horizontalAlign=null;
+ private $_verticalAlign=null;
+ private $_wrap=null;
+ public function reset()
+ {
+ parent::reset();
+ $this->_verticalAlign=null;
+ $this->_horizontalAlign=null;
+ $this->_wrap=null;
+ }
+ public function copyFrom($style)
+ {
+ parent::copyFrom($style);
+ if($style instanceof TTableItemStyle)
+ {
+ if($this->_verticalAlign===null && $style->_verticalAlign!==null)
+ $this->_verticalAlign=$style->_verticalAlign;
+ if($this->_horizontalAlign===null && $style->_horizontalAlign!==null)
+ $this->_horizontalAlign=$style->_horizontalAlign;
+ if($this->_wrap===null && $style->_wrap!==null)
+ $this->_wrap=$style->_wrap;
+ }
+ }
+ public function mergeWith($style)
+ {
+ parent::mergeWith($style);
+ if($style instanceof TTableItemStyle)
+ {
+ if($style->_verticalAlign!==null)
+ $this->_verticalAlign=$style->_verticalAlign;
+ if($style->_horizontalAlign!==null)
+ $this->_horizontalAlign=$style->_horizontalAlign;
+ if($style->_wrap!==null)
+ $this->_wrap=$style->_wrap;
+ }
+ }
+ public function addAttributesToRender($writer)
+ {
+ if(!$this->getWrap())
+ $writer->addStyleAttribute('white-space','nowrap');
+ if(($horizontalAlign=$this->getHorizontalAlign())!==THorizontalAlign::NotSet)
+ $writer->addAttribute('align',strtolower($horizontalAlign));
+ if(($verticalAlign=$this->getVerticalAlign())!==TVerticalAlign::NotSet)
+ $writer->addAttribute('valign',strtolower($verticalAlign));
+ parent::addAttributesToRender($writer);
+ }
+ public function getHorizontalAlign()
+ {
+ return $this->_horizontalAlign===null?THorizontalAlign::NotSet:$this->_horizontalAlign;
+ }
+ public function setHorizontalAlign($value)
+ {
+ $this->_horizontalAlign=TPropertyValue::ensureEnum($value,'THorizontalAlign');
+ }
+ public function getVerticalAlign()
+ {
+ return $this->_verticalAlign===null?TVerticalAlign::NotSet:$this->_verticalAlign;
+ }
+ public function setVerticalAlign($value)
+ {
+ $this->_verticalAlign=TPropertyValue::ensureEnum($value,'TVerticalAlign');
+ }
+ public function getWrap()
+ {
+ return $this->_wrap===null?true:$this->_wrap;
+ }
+ public function setWrap($value)
+ {
+ $this->_wrap=TPropertyValue::ensureBoolean($value);
+ }
+}
+class THorizontalAlign extends TEnumerable
+{
+ const NotSet='NotSet';
+ const Left='Left';
+ const Right='Right';
+ const Center='Center';
+ const Justify='Justify';
+}
+class TVerticalAlign extends TEnumerable
+{
+ const NotSet='NotSet';
+ const Top='Top';
+ const Bottom='Bottom';
+ const Middle='Middle';
+}
+class TTableGridLines extends TEnumerable
+{
+ const None='None';
+ const Horizontal='Horizontal';
+ const Vertical='Vertical';
+ const Both='Both';
+}
+class TWebControlAdapter extends TControlAdapter
+{
+ public function render($writer)
+ {
+ $this->renderBeginTag($writer);
+ $this->renderContents($writer);
+ $this->renderEndTag($writer);
+ }
+ public function renderBeginTag($writer)
+ {
+ $this->getControl()->renderBeginTag($writer);
+ }
+ public function renderContents($writer)
+ {
+ $this->getControl()->renderContents($writer);
+ }
+ public function renderEndTag($writer)
+ {
+ $this->getControl()->renderEndTag($writer);
+ }
+}
+class TWebControl extends TControl implements IStyleable
+{
+ public function copyBaseAttributes(TWebControl $control)
+ {
+ $this->setAccessKey($control->getAccessKey());
+ $this->setToolTip($control->getToolTip());
+ $this->setTabIndex($control->getTabIndex());
+ if(!$control->getEnabled())
+ $this->setEnabled(false);
+ if($control->getHasAttributes())
+ $this->getAttributes()->copyFrom($control->getAttributes());
+ }
+ public function getAccessKey()
+ {
+ return $this->getViewState('AccessKey','');
+ }
+ public function setAccessKey($value)
+ {
+ if(strlen($value)>1)
+ throw new TInvalidDataValueException('webcontrol_accesskey_invalid',get_class($this),$value);
+ $this->setViewState('AccessKey',$value,'');
+ }
+ public function getBackColor()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style->getBackColor();
+ else
+ return '';
+ }
+ public function setBackColor($value)
+ {
+ $this->getStyle()->setBackColor($value);
+ }
+ public function getBorderColor()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style->getBorderColor();
+ else
+ return '';
+ }
+ public function setBorderColor($value)
+ {
+ $this->getStyle()->setBorderColor($value);
+ }
+ public function getBorderStyle()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style->getBorderStyle();
+ else
+ return '';
+ }
+ public function setBorderStyle($value)
+ {
+ $this->getStyle()->setBorderStyle($value);
+ }
+ public function getBorderWidth()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style->getBorderWidth();
+ else
+ return '';
+ }
+ public function setBorderWidth($value)
+ {
+ $this->getStyle()->setBorderWidth($value);
+ }
+ public function getFont()
+ {
+ return $this->getStyle()->getFont();
+ }
+ public function getForeColor()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style->getForeColor();
+ else
+ return '';
+ }
+ public function setForeColor($value)
+ {
+ $this->getStyle()->setForeColor($value);
+ }
+ public function getHeight()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style->getHeight();
+ else
+ return '';
+ }
+ public function setDisplay($value)
+ {
+ $this->getStyle()->setDisplayStyle($value);
+ }
+ public function getDisplay()
+ {
+ return $this->getStyle()->getDisplayStyle();
+ }
+ public function setCssClass($value)
+ {
+ $this->getStyle()->setCssClass($value);
+ }
+ public function getCssClass()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style->getCssClass();
+ else
+ return '';
+ }
+ public function setHeight($value)
+ {
+ $this->getStyle()->setHeight($value);
+ }
+ public function getHasStyle()
+ {
+ return $this->getViewState('Style',null)!==null;
+ }
+ protected function createStyle()
+ {
+ return new TStyle;
+ }
+ public function getStyle()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style;
+ else
+ {
+ $style=$this->createStyle();
+ $this->setViewState('Style',$style,null);
+ return $style;
+ }
+ }
+ public function setStyle($value)
+ {
+ if(is_string($value))
+ $this->getStyle()->setCustomStyle($value);
+ else
+ throw new TInvalidDataValueException('webcontrol_style_invalid',get_class($this));
+ }
+ public function clearStyle()
+ {
+ $this->clearViewState('Style');
+ }
+ public function getTabIndex()
+ {
+ return $this->getViewState('TabIndex',0);
+ }
+ public function setTabIndex($value)
+ {
+ $this->setViewState('TabIndex',TPropertyValue::ensureInteger($value),0);
+ }
+ protected function getTagName()
+ {
+ return 'span';
+ }
+ public function getToolTip()
+ {
+ return $this->getViewState('ToolTip','');
+ }
+ public function setToolTip($value)
+ {
+ $this->setViewState('ToolTip',$value,'');
+ }
+ public function getWidth()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style->getWidth();
+ else
+ return '';
+ }
+ public function setWidth($value)
+ {
+ $this->getStyle()->setWidth($value);
+ }
+ protected function addAttributesToRender($writer)
+ {
+ if($this->getID()!=='')
+ $writer->addAttribute('id',$this->getClientID());
+ if(($accessKey=$this->getAccessKey())!=='')
+ $writer->addAttribute('accesskey',$accessKey);
+ if(!$this->getEnabled())
+ $writer->addAttribute('disabled','disabled');
+ if(($tabIndex=$this->getTabIndex())>0)
+ $writer->addAttribute('tabindex',"$tabIndex");
+ if(($toolTip=$this->getToolTip())!=='')
+ $writer->addAttribute('title',$toolTip);
+ if($style=$this->getViewState('Style',null))
+ $style->addAttributesToRender($writer);
+ if($this->getHasAttributes())
+ {
+ foreach($this->getAttributes() as $name=>$value)
+ $writer->addAttribute($name,$value);
+ }
+ }
+ public function render($writer)
+ {
+ $this->renderBeginTag($writer);
+ $this->renderContents($writer);
+ $this->renderEndTag($writer);
+ }
+ public function renderBeginTag($writer)
+ {
+ $this->addAttributesToRender($writer);
+ $writer->renderBeginTag($this->getTagName());
+ }
+ public function renderContents($writer)
+ {
+ parent::renderChildren($writer);
+ }
+ public function renderEndTag($writer)
+ {
+ $writer->renderEndTag();
+ }
+}
+class TCompositeControl extends TControl implements INamingContainer
+{
+ protected function initRecursive($namingContainer=null)
+ {
+ $this->ensureChildControls();
+ parent::initRecursive($namingContainer);
+ }
+}
+class TTemplateControl extends TCompositeControl
+{
+ const EXT_TEMPLATE='.tpl';
+ private static $_template=array();
+ private $_localTemplate=null;
+ private $_master=null;
+ private $_masterClass='';
+ private $_contents=array();
+ private $_placeholders=array();
+ public function getTemplate()
+ {
+ if($this->_localTemplate===null)
+ {
+ $class=get_class($this);
+ if(!isset(self::$_template[$class]))
+ self::$_template[$class]=$this->loadTemplate();
+ return self::$_template[$class];
+ }
+ else
+ return $this->_localTemplate;
+ }
+ public function setTemplate($value)
+ {
+ $this->_localTemplate=$value;
+ }
+ public function getIsSourceTemplateControl()
+ {
+ if(($template=$this->getTemplate())!==null)
+ return $template->getIsSourceTemplate();
+ else
+ return false;
+ }
+ public function getTemplateDirectory()
+ {
+ if(($template=$this->getTemplate())!==null)
+ return $template->getContextPath();
+ else
+ return '';
+ }
+ protected function loadTemplate()
+ {
+ $template=$this->getService()->getTemplateManager()->getTemplateByClassName(get_class($this));
+ return $template;
+ }
+ public function createChildControls()
+ {
+ if($tpl=$this->getTemplate())
+ {
+ foreach($tpl->getDirective() as $name=>$value)
+ {
+ if(is_string($value))
+ $this->setSubProperty($name,$value);
+ else
+ throw new TConfigurationException('templatecontrol_directive_invalid',get_class($this),$name);
+ }
+ $tpl->instantiateIn($this);
+ }
+ }
+ public function registerContent($id,TContent $object)
+ {
+ if(isset($this->_contents[$id]))
+ throw new TConfigurationException('templatecontrol_contentid_duplicated',$id);
+ else
+ $this->_contents[$id]=$object;
+ }
+ public function registerContentPlaceHolder($id,TContentPlaceHolder $object)
+ {
+ if(isset($this->_placeholders[$id]))
+ throw new TConfigurationException('templatecontrol_placeholderid_duplicated',$id);
+ else
+ $this->_placeholders[$id]=$object;
+ }
+ public function getMasterClass()
+ {
+ return $this->_masterClass;
+ }
+ public function setMasterClass($value)
+ {
+ $this->_masterClass=$value;
+ }
+ public function getMaster()
+ {
+ return $this->_master;
+ }
+ public function injectContent($id,$content)
+ {
+ if(isset($this->_placeholders[$id]))
+ {
+ $placeholder=$this->_placeholders[$id];
+ $controls=$placeholder->getParent()->getControls();
+ $loc=$controls->remove($placeholder);
+ $controls->insertAt($loc,$content);
+ }
+ else
+ throw new TConfigurationException('templatecontrol_placeholder_inexistent',$id);
+ }
+ protected function initRecursive($namingContainer=null)
+ {
+ $this->ensureChildControls();
+ if($this->_masterClass!=='')
+ {
+ $master=Prado::createComponent($this->_masterClass);
+ if(!($master instanceof TTemplateControl))
+ throw new TInvalidDataValueException('templatecontrol_mastercontrol_invalid');
+ $this->_master=$master;
+ $this->getControls()->clear();
+ $this->getControls()->add($master);
+ $master->ensureChildControls();
+ foreach($this->_contents as $id=>$content)
+ $master->injectContent($id,$content);
+ }
+ else if(!empty($this->_contents))
+ throw new TConfigurationException('templatecontrol_mastercontrol_required',get_class($this));
+ parent::initRecursive($namingContainer);
+ }
+}
+class TForm extends TControl
+{
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ $this->getPage()->setForm($this);
+ }
+ protected function addAttributesToRender($writer)
+ {
+ $writer->addAttribute('id',$this->getClientID());
+ $writer->addAttribute('method',$this->getMethod());
+ $uri=$this->getRequest()->getRequestURI();
+ $writer->addAttribute('action',str_replace('&','&amp;',str_replace('&amp;','&',$uri)));
+ if(($enctype=$this->getEnctype())!=='')
+ $writer->addAttribute('enctype',$enctype);
+ $attributes=$this->getAttributes();
+ $attributes->remove('action');
+ $writer->addAttributes($attributes);
+ if(($butt=$this->getDefaultButton())!=='')
+ {
+ if(($button=$this->findControl($butt))!==null)
+ $this->getPage()->getClientScript()->registerDefaultButton($this, $button);
+ else
+ throw new TInvalidDataValueException('form_defaultbutton_invalid',$butt);
+ }
+ }
+ public function render($writer)
+ {
+ $page=$this->getPage();
+ $page->beginFormRender($writer);
+ $textWriter=new TTextWriter;
+ $this->renderChildren(new THtmlWriter($textWriter));
+ $content=$textWriter->flush();
+ $page->endFormRender($writer);
+ $this->addAttributesToRender($writer);
+ $writer->renderBeginTag('form');
+ $cs=$page->getClientScript();
+ if($page->getClientSupportsJavaScript())
+ {
+ $cs->renderHiddenFields($writer);
+ $cs->renderScriptFiles($writer);
+ $cs->renderBeginScripts($writer);
+ $writer->write($content);
+ $cs->renderEndScripts($writer);
+ }
+ else
+ {
+ $cs->renderHiddenFields($writer);
+ $writer->write($content);
+ }
+ $writer->renderEndTag();
+ }
+ public function getDefaultButton()
+ {
+ return $this->getViewState('DefaultButton','');
+ }
+ public function setDefaultButton($value)
+ {
+ $this->setViewState('DefaultButton',$value,'');
+ }
+ public function getMethod()
+ {
+ return $this->getViewState('Method','post');
+ }
+ public function setMethod($value)
+ {
+ $this->setViewState('Method',TPropertyValue::ensureEnum($value,'post','get'),'post');
+ }
+ public function getEnctype()
+ {
+ return $this->getViewState('Enctype','');
+ }
+ public function setEnctype($value)
+ {
+ $this->setViewState('Enctype',$value,'');
+ }
+ public function getName()
+ {
+ return $this->getUniqueID();
+ }
+}
+class TClientScriptManager extends TApplicationComponent
+{
+ const SCRIPT_PATH='Web/Javascripts/source';
+ const SCRIPT_LOADER='Web/Javascripts/clientscripts.php';
+ private $_page;
+ private $_hiddenFields=array();
+ private $_beginScripts=array();
+ private $_endScripts=array();
+ private $_scriptFiles=array();
+ private $_headScriptFiles=array();
+ private $_headScripts=array();
+ private $_styleSheetFiles=array();
+ private $_styleSheets=array();
+ private $_registeredPradoScripts=array();
+ private static $_pradoScripts;
+ private static $_pradoPackages;
+ public function __construct(TPage $owner)
+ {
+ $this->_page=$owner;
+ }
+ public function getRequiresHead()
+ {
+ return count($this->_styleSheetFiles) || count($this->_styleSheets)
+ || count($this->_headScriptFiles) || count($this->_headScripts);
+ }
+ public function registerPradoScript($name)
+ {
+ $this->registerPradoScriptInternal($name);
+ $params=func_get_args();
+ $this->_page->registerCachingAction('Page.ClientScript','registerPradoScript',$params);
+ }
+ private function registerPradoScriptInternal($name)
+ {
+ if(!isset($this->_registeredPradoScripts[$name]))
+ {
+ if(self::$_pradoScripts === null)
+ {
+ $packageFile = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH.'/packages.php';
+ list($packages,$deps)= include($packageFile);
+ self::$_pradoScripts = $deps;
+ self::$_pradoPackages = $packages;
+ }
+ if(isset(self::$_pradoScripts[$name]))
+ $this->_registeredPradoScripts[$name]=true;
+ else
+ throw new TInvalidOperationException('csmanager_pradoscript_invalid',$name);
+ }
+ }
+ public function getPradoScriptAssetUrl()
+ {
+ $base = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH;
+ $assets = Prado::getApplication()->getAssetManager();
+ return $assets->getPublishedUrl($base);
+ }
+ protected function renderPradoScripts($writer)
+ {
+ if(($packages=array_keys($this->_registeredPradoScripts))!==array())
+ {
+ if (Prado::getApplication()->getMode()!==TApplicationMode::Debug)
+ {
+ $base = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH;
+ $url = $this->registerJavascriptPackages($base, $packages);
+ $writer->write(TJavaScript::renderScriptFile($url));
+ }
+ else
+ {
+ $base = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH;
+ list($path,$baseUrl)=$this->getPackagePathUrl($base);
+ $packagesUrl=array();
+ foreach ($packages as $p)
+ {
+ foreach (self::$_pradoScripts[$p] as $dep)
+ {
+ foreach (self::$_pradoPackages[$dep] as $script)
+ {
+ if (!in_array($url=$baseUrl.'/'.$script,$packagesUrl))
+ $packagesUrl[]=$url;
+ }
+ }
+ }
+ $writer->write(TJavaScript::renderScriptFiles($packagesUrl));
+ }
+ }
+ }
+ public function registerJavascriptPackages($base, $packages, $debug=null, $gzip=true)
+ {
+ list($path,$url) = $this->getPackagePathUrl($base);
+ $scriptLoaderPath = $path.'/'.basename(self::SCRIPT_LOADER);
+ $scriptLoaderSrc = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_LOADER;
+ if(!is_file($scriptLoaderPath))
+ {
+ copy($scriptLoaderSrc, $scriptLoaderPath);
+ chmod($scriptLoaderPath, PRADO_CHMOD);
+ }
+ $url .= '/'.basename(self::SCRIPT_LOADER).'?js='.implode(',', $packages);
+ if($debug!==false && $this->getApplication()->getMode()===TApplicationMode::Debug)
+ {
+ $this->verifyJavascriptPackages($base,$path,$packages);
+ $url.='&amp;mode=debug';
+ }
+ if($gzip===false)
+ $url.='&amp;gzip=false';
+ return $url;
+ }
+ protected function verifyJavascriptPackages($base,$path,$scripts)
+ {
+ $file = $path.'/packages.php';
+ if(is_file($file))
+ {
+ list($packs,$deps) = include($file);
+ if(count($missing = array_diff($scripts, array_keys($deps))) > 0)
+ {
+ throw new TConfigurationException('csmanager_invalid_packages',
+ $base.'/packages.php',implode(', ', $missing), implode(', ', array_keys($deps)));
+ }
+ }
+ }
+ protected function getPackagePathUrl($base)
+ {
+ $assets = Prado::getApplication()->getAssetManager();
+ if(strpos($base, $assets->getBaseUrl())===false)
+ {
+ if(($dir = Prado::getPathOfNameSpace($base)) !== null) {
+ $base = $dir;
+ }
+ return array($assets->getPublishedPath($base), $assets->publishFilePath($base));
+ }
+ else
+ {
+ return array($assets->getBasePath().str_replace($assets->getBaseUrl(),'',$base), $base);
+ }
+ }
+ public function getCallbackReference(ICallbackEventHandler $callbackHandler, $options=null)
+ {
+ $options = !is_array($options) ? array() : $options;
+ $class = new TReflectionClass($callbackHandler);
+ $clientSide = $callbackHandler->getActiveControl()->getClientSide();
+ $options = array_merge($options, $clientSide->getOptions()->toArray());
+ $optionString = TJavaScript::encode($options);
+ $this->registerPradoScriptInternal('ajax');
+ $id = $callbackHandler->getUniqueID();
+ return "new Prado.CallbackRequest('{$id}',{$optionString})";
+ }
+ public function registerCallbackControl($class, $options)
+ {
+ $optionString=TJavaScript::encode($options);
+ $code="new {$class}({$optionString});";
+ $this->_endScripts[sprintf('%08X', crc32($code))]=$code;
+ $this->registerPradoScriptInternal('ajax');
+ $params=func_get_args();
+ $this->_page->registerCachingAction('Page.ClientScript','registerCallbackControl',$params);
+ }
+ public function registerPostBackControl($class,$options)
+ {
+ if($class === null) {
+ return;
+ }
+ if(!isset($options['FormID']) && ($form=$this->_page->getForm())!==null)
+ $options['FormID']=$form->getClientID();
+ $optionString=TJavaScript::encode($options);
+ $code="new {$class}({$optionString});";
+ $this->_endScripts[sprintf('%08X', crc32($code))]=$code;
+ $this->_hiddenFields[TPage::FIELD_POSTBACK_TARGET]='';
+ $this->_hiddenFields[TPage::FIELD_POSTBACK_PARAMETER]='';
+ $this->registerPradoScriptInternal('prado');
+ $params=func_get_args();
+ $this->_page->registerCachingAction('Page.ClientScript','registerPostBackControl',$params);
+ }
+ public function registerDefaultButton($panel, $button)
+ {
+ $panelID=is_string($panel)?$panel:$panel->getUniqueID();
+ if(is_string($button))
+ $buttonID=$button;
+ else
+ {
+ $button->setIsDefaultButton(true);
+ $buttonID=$button->getUniqueID();
+ }
+ $options = TJavaScript::encode($this->getDefaultButtonOptions($panelID, $buttonID));
+ $code = "new Prado.WebUI.DefaultButton($options);";
+ $this->_endScripts['prado:'.$panelID]=$code;
+ $this->_hiddenFields[TPage::FIELD_POSTBACK_TARGET]='';
+ $this->registerPradoScriptInternal('prado');
+ $params=array($panelID,$buttonID);
+ $this->_page->registerCachingAction('Page.ClientScript','registerDefaultButton',$params);
+ }
+ protected function getDefaultButtonOptions($panelID, $buttonID)
+ {
+ $options['Panel'] = TControl::convertUniqueIdToClientId($panelID);
+ $options['Target'] = TControl::convertUniqueIdToClientId($buttonID);
+ $options['EventTarget'] = $buttonID;
+ $options['Event'] = 'click';
+ return $options;
+ }
+ public function registerFocusControl($target)
+ {
+ $this->registerPradoScriptInternal('effects');
+ if($target instanceof TControl)
+ $target=$target->getClientID();
+ $id = TJavaScript::quoteString($target);
+ $this->_endScripts['prado:focus'] = 'new Effect.ScrollTo("'.$id.'"); Prado.Element.focus("'.$id.'");';
+ $params=func_get_args();
+ $this->_page->registerCachingAction('Page.ClientScript','registerFocusControl',$params);
+ }
+ public function registerStyleSheetFile($key,$url,$media='')
+ {
+ if($media==='')
+ $this->_styleSheetFiles[$key]=$url;
+ else
+ $this->_styleSheetFiles[$key]=array($url,$media);
+ $params=func_get_args();
+ $this->_page->registerCachingAction('Page.ClientScript','registerStyleSheetFile',$params);
+ }
+ public function registerStyleSheet($key,$css,$media='')
+ {
+ $this->_styleSheets[$key]=$css;
+ $params=func_get_args();
+ $this->_page->registerCachingAction('Page.ClientScript','registerStyleSheet',$params);
+ }
+ public function registerHeadScriptFile($key,$url)
+ {
+ $this->_headScriptFiles[$key]=$url;
+ $params=func_get_args();
+ $this->_page->registerCachingAction('Page.ClientScript','registerHeadScriptFile',$params);
+ }
+ public function registerHeadScript($key,$script)
+ {
+ $this->_headScripts[$key]=$script;
+ $params=func_get_args();
+ $this->_page->registerCachingAction('Page.ClientScript','registerHeadScript',$params);
+ }
+ public function registerScriptFile($key,$url)
+ {
+ $this->_scriptFiles[$key]=$url;
+ $params=func_get_args();
+ $this->_page->registerCachingAction('Page.ClientScript','registerScriptFile',$params);
+ }
+ public function registerBeginScript($key,$script)
+ {
+ $this->_beginScripts[$key]=$script;
+ $params=func_get_args();
+ $this->_page->registerCachingAction('Page.ClientScript','registerBeginScript',$params);
+ }
+ public function registerEndScript($key,$script)
+ {
+ $this->_endScripts[$key]=$script;
+ $params=func_get_args();
+ $this->_page->registerCachingAction('Page.ClientScript','registerEndScript',$params);
+ }
+ public function registerHiddenField($name,$value)
+ {
+ $this->_hiddenFields[$name]=$value;
+ $params=func_get_args();
+ $this->_page->registerCachingAction('Page.ClientScript','registerHiddenField',$params);
+ }
+ public function isStyleSheetFileRegistered($key)
+ {
+ return isset($this->_styleSheetFiles[$key]);
+ }
+ public function isStyleSheetRegistered($key)
+ {
+ return isset($this->_styleSheets[$key]);
+ }
+ public function isHeadScriptFileRegistered($key)
+ {
+ return isset($this->_headScriptFiles[$key]);
+ }
+ public function isHeadScriptRegistered($key)
+ {
+ return isset($this->_headScripts[$key]);
+ }
+ public function isScriptFileRegistered($key)
+ {
+ return isset($this->_scriptFiles[$key]);
+ }
+ public function isBeginScriptRegistered($key)
+ {
+ return isset($this->_beginScripts[$key]);
+ }
+ public function isEndScriptRegistered($key)
+ {
+ return isset($this->_endScripts[$key]);
+ }
+ public function hasEndScripts()
+ {
+ return count($this->_endScripts) > 0;
+ }
+ public function hasBeginScripts()
+ {
+ return count($this->_beginScripts) > 0;
+ }
+ public function isHiddenFieldRegistered($key)
+ {
+ return isset($this->_hiddenFields[$key]);
+ }
+ public function renderStyleSheetFiles($writer)
+ {
+ $str='';
+ foreach($this->_styleSheetFiles as $url)
+ {
+ if(is_array($url))
+ $str.="<link rel=\"stylesheet\" type=\"text/css\" media=\"{$url[1]}\" href=\"".THttpUtility::htmlEncode($url[0])."\" />\n";
+ else
+ $str.="<link rel=\"stylesheet\" type=\"text/css\" href=\"".THttpUtility::htmlEncode($url)."\" />\n";
+ }
+ $writer->write($str);
+ }
+ public function renderStyleSheets($writer)
+ {
+ if(count($this->_styleSheets))
+ $writer->write("<style type=\"text/css\">\n/*<![CDATA[*/\n".implode("\n",$this->_styleSheets)."\n/*]]>*/\n</style>\n");
+ }
+ public function renderHeadScriptFiles($writer)
+ {
+ $writer->write(TJavaScript::renderScriptFiles($this->_headScriptFiles));
+ }
+ public function renderHeadScripts($writer)
+ {
+ $writer->write(TJavaScript::renderScriptBlocks($this->_headScripts));
+ }
+ public function renderScriptFiles($writer)
+ {
+ $this->renderPradoScripts($writer);
+ if(!empty($this->_scriptFiles))
+ $writer->write(TJavaScript::renderScriptFiles($this->_scriptFiles));
+ }
+ public function renderBeginScripts($writer)
+ {
+ $writer->write(TJavaScript::renderScriptBlocks($this->_beginScripts));
+ }
+ public function renderEndScripts($writer)
+ {
+ $writer->write(TJavaScript::renderScriptBlocks($this->_endScripts));
+ }
+ public function renderHiddenFields($writer)
+ {
+ $str='';
+ foreach($this->_hiddenFields as $name=>$value)
+ {
+ $id=strtr($name,':','_');
+ if(is_array($value))
+ {
+ foreach($value as $v)
+ $str.='<input type="hidden" name="'.$name.'[]" id="'.$id.'" value="'.THttpUtility::htmlEncode($value)."\" />\n";
+ }
+ else
+ {
+ $str.='<input type="hidden" name="'.$name.'" id="'.$id.'" value="'.THttpUtility::htmlEncode($value)."\" />\n";
+ }
+ }
+ if($str!=='')
+ $writer->write("<div style=\"visibility:hidden;\">\n".$str."</div>\n");
+ }
+}
+abstract class TClientSideOptions extends TComponent
+{
+ private $_options;
+ public function __construct()
+ {
+ $this->_options = Prado::createComponent('System.Collections.TMap');
+ }
+ protected function setFunction($name, $code)
+ {
+ if(!TJavaScript::isFunction($code))
+ $code = TJavaScript::quoteFunction($this->ensureFunction($code));
+ $this->setOption($name, $code);
+ }
+ protected function getOption($name)
+ {
+ return $this->_options->itemAt($name);
+ }
+ protected function setOption($name, $value)
+ {
+ $this->_options->add($name, $value);
+ }
+ public function getOptions()
+ {
+ return $this->_options;
+ }
+ protected function ensureFunction($javascript)
+ {
+ return "function(sender, parameter){ {$javascript} }";
+ }
+}
+class TPage extends TTemplateControl
+{
+ const FIELD_POSTBACK_TARGET='PRADO_POSTBACK_TARGET';
+ const FIELD_POSTBACK_PARAMETER='PRADO_POSTBACK_PARAMETER';
+ const FIELD_LASTFOCUS='PRADO_LASTFOCUS';
+ const FIELD_PAGESTATE='PRADO_PAGESTATE';
+ const FIELD_CALLBACK_TARGET='PRADO_CALLBACK_TARGET';
+ const FIELD_CALLBACK_PARAMETER='PRADO_CALLBACK_PARAMETER';
+ private static $_systemPostFields=array(
+ 'PRADO_POSTBACK_TARGET'=>true,
+ 'PRADO_POSTBACK_PARAMETER'=>true,
+ 'PRADO_LASTFOCUS'=>true,
+ 'PRADO_PAGESTATE'=>true,
+ 'PRADO_CALLBACK_TARGET'=>true,
+ 'PRADO_CALLBACK_PARAMETER'=>true
+ );
+ private $_form;
+ private $_head;
+ private $_validators=array();
+ private $_validated=false;
+ private $_theme;
+ private $_title;
+ private $_styleSheet;
+ private $_clientScript;
+ private $_postData;
+ private $_restPostData;
+ private $_controlsPostDataChanged=array();
+ private $_controlsRequiringPostData=array();
+ private $_controlsRegisteredForPostData=array();
+ private $_postBackEventTarget;
+ private $_postBackEventParameter;
+ private $_formRendered=false;
+ private $_inFormRender=false;
+ private $_focus;
+ private $_pagePath='';
+ private $_enableStateValidation=true;
+ private $_enableStateEncryption=false;
+ private $_statePersisterClass='System.Web.UI.TPageStatePersister';
+ private $_statePersister;
+ private $_cachingStack;
+ private $_clientState='';
+ private $_postDataLoaders=array();
+ private $_isLoadingPostData=false;
+ private $_enableJavaScript=true;
+ public function __construct()
+ {
+ parent::__construct();
+ $this->setPage($this);
+ }
+ public function run($writer)
+ {
+ $this->determinePostBackMode();
+ if($this->getIsPostBack())
+ {
+ if($this->getIsCallback())
+ $this->processCallbackRequest($writer);
+ else
+ $this->processPostBackRequest($writer);
+ }
+ else
+ $this->processNormalRequest($writer);
+ }
+ protected function processNormalRequest($writer)
+ {
+ $this->onPreInit(null);
+ $this->initRecursive();
+ $this->onInitComplete(null);
+ $this->onPreLoad(null);
+ $this->loadRecursive();
+ $this->onLoadComplete(null);
+ $this->preRenderRecursive();
+ $this->onPreRenderComplete(null);
+ $this->savePageState();
+ $this->onSaveStateComplete(null);
+ $this->renderControl($writer);
+ $this->unloadRecursive();
+ }
+ protected function processPostBackRequest($writer)
+ {
+ $this->onPreInit(null);
+ $this->initRecursive();
+ $this->onInitComplete(null);
+ $this->_restPostData=new TMap;
+ $this->loadPageState();
+ $this->processPostData($this->_postData,true);
+ $this->onPreLoad(null);
+ $this->loadRecursive();
+ $this->processPostData($this->_restPostData,false);
+ $this->raiseChangedEvents();
+ $this->raisePostBackEvent();
+ $this->onLoadComplete(null);
+ $this->preRenderRecursive();
+ $this->onPreRenderComplete(null);
+ $this->savePageState();
+ $this->onSaveStateComplete(null);
+ $this->renderControl($writer);
+ $this->unloadRecursive();
+ }
+ protected function processCallbackRequest($writer)
+ {
+ Prado::using('System.Web.UI.ActiveControls.TActivePageAdapter');
+ $this->setAdapter(new TActivePageAdapter($this));
+ if (($g=$this->getApplication()->getGlobalization(false))!==null &&
+ strtoupper($enc=$g->getCharset())!='UTF-8')
+ foreach ($this->_postData as $k=>$v)
+ $this->_postData[$k]=iconv('UTF-8',$enc.'//IGNORE',$v);
+ $this->onPreInit(null);
+ $this->initRecursive();
+ $this->onInitComplete(null);
+ $this->_restPostData=new TMap;
+ $this->loadPageState();
+ $this->processPostData($this->_postData,true);
+ $this->onPreLoad(null);
+ $this->loadRecursive();
+ $this->processPostData($this->_restPostData,false);
+ $this->raiseChangedEvents();
+ $this->getAdapter()->processCallbackEvent($writer);
+ $this->onLoadComplete(null);
+ $this->preRenderRecursive();
+ $this->onPreRenderComplete(null);
+ $this->savePageState();
+ $this->onSaveStateComplete(null);
+ $this->getAdapter()->renderCallbackResponse($writer);
+ $this->unloadRecursive();
+ }
+ public function getCallbackClient()
+ {
+ if($this->getAdapter() !== null)
+ return $this->getAdapter()->getCallbackClientHandler();
+ else
+ return new TCallbackClientScript();
+ }
+ public function setCallbackClient($client)
+ {
+ $this->getAdapter()->setCallbackClientHandler($client);
+ }
+ public function getCallbackEventTarget()
+ {
+ return $this->getAdapter()->getCallbackEventTarget();
+ }
+ public function setCallbackEventTarget(TControl $control)
+ {
+ $this->getAdapter()->setCallbackEventTarget($control);
+ }
+ public function getCallbackEventParameter()
+ {
+ return $this->getAdapter()->getCallbackEventParameter();
+ }
+ public function setCallbackEventParameter($value)
+ {
+ $this->getAdapter()->setCallbackEventParameter($value);
+ }
+ public function registerPostDataLoader($control)
+ {
+ $id=is_string($control)?$control:$control->getUniqueID();
+ $this->_postDataLoaders[$id] = true;
+ }
+ public function getPostDataLoaders()
+ {
+ return array_keys($this->_postDataLoaders);
+ }
+ public function getForm()
+ {
+ return $this->_form;
+ }
+ public function setForm(TForm $form)
+ {
+ if($this->_form===null)
+ $this->_form=$form;
+ else
+ throw new TInvalidOperationException('page_form_duplicated');
+ }
+ public function getValidators($validationGroup=null)
+ {
+ if(!$this->_validators)
+ $this->_validators=new TList;
+ if($validationGroup===null)
+ return $this->_validators;
+ else
+ {
+ $list=new TList;
+ foreach($this->_validators as $validator)
+ if($validator->getValidationGroup()===$validationGroup)
+ $list->add($validator);
+ return $list;
+ }
+ }
+ public function validate($validationGroup=null)
+ {
+ $this->_validated=true;
+ if($this->_validators && $this->_validators->getCount())
+ {
+ if($validationGroup===null)
+ {
+ foreach($this->_validators as $validator)
+ $validator->validate();
+ }
+ else
+ {
+ foreach($this->_validators as $validator)
+ {
+ if($validator->getValidationGroup()===$validationGroup)
+ $validator->validate();
+ }
+ }
+ }
+ }
+ public function getIsValid()
+ {
+ if($this->_validated)
+ {
+ if($this->_validators && $this->_validators->getCount())
+ {
+ foreach($this->_validators as $validator)
+ if(!$validator->getIsValid())
+ return false;
+ }
+ return true;
+ }
+ else
+ throw new TInvalidOperationException('page_isvalid_unknown');
+ }
+ public function getTheme()
+ {
+ if(is_string($this->_theme))
+ $this->_theme=$this->getService()->getThemeManager()->getTheme($this->_theme);
+ return $this->_theme;
+ }
+ public function setTheme($value)
+ {
+ $this->_theme=empty($value)?null:$value;
+ }
+ public function getStyleSheetTheme()
+ {
+ if(is_string($this->_styleSheet))
+ $this->_styleSheet=$this->getService()->getThemeManager()->getTheme($this->_styleSheet);
+ return $this->_styleSheet;
+ }
+ public function setStyleSheetTheme($value)
+ {
+ $this->_styleSheet=empty($value)?null:$value;
+ }
+ public function applyControlSkin($control)
+ {
+ if(($theme=$this->getTheme())!==null)
+ $theme->applySkin($control);
+ }
+ public function applyControlStyleSheet($control)
+ {
+ if(($theme=$this->getStyleSheetTheme())!==null)
+ $theme->applySkin($control);
+ }
+ public function getClientScript()
+ {
+ if(!$this->_clientScript)
+ $this->_clientScript=new TClientScriptManager($this);
+ return $this->_clientScript;
+ }
+ public function onPreInit($param)
+ {
+ $this->raiseEvent('OnPreInit',$this,$param);
+ }
+ public function onInitComplete($param)
+ {
+ $this->raiseEvent('OnInitComplete',$this,$param);
+ }
+ public function onPreLoad($param)
+ {
+ $this->raiseEvent('OnPreLoad',$this,$param);
+ }
+ public function onLoadComplete($param)
+ {
+ $this->raiseEvent('OnLoadComplete',$this,$param);
+ }
+ public function onPreRenderComplete($param)
+ {
+ $this->raiseEvent('OnPreRenderComplete',$this,$param);
+ $cs=$this->getClientScript();
+ $theme=$this->getTheme();
+ if($theme instanceof ITheme)
+ {
+ foreach($theme->getStyleSheetFiles() as $url)
+ $cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url));
+ foreach($theme->getJavaScriptFiles() as $url)
+ $cs->registerHeadScriptFile($url,$url);
+ }
+ $styleSheet=$this->getStyleSheetTheme();
+ if($styleSheet instanceof ITheme)
+ {
+ foreach($styleSheet->getStyleSheetFiles() as $url)
+ $cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url));
+ foreach($styleSheet->getJavaScriptFiles() as $url)
+ $cs->registerHeadScriptFile($url,$url);
+ }
+ if($cs->getRequiresHead() && $this->getHead()===null)
+ throw new TConfigurationException('page_head_required');
+ }
+ private function getCssMediaType($url)
+ {
+ $segs=explode('.',basename($url));
+ if(isset($segs[2]))
+ return $segs[count($segs)-2];
+ else
+ return '';
+ }
+ public function onSaveStateComplete($param)
+ {
+ $this->raiseEvent('OnSaveStateComplete',$this,$param);
+ }
+ private function determinePostBackMode()
+ {
+ $postData=$this->getRequest();
+ if($postData->contains(self::FIELD_PAGESTATE) || $postData->contains(self::FIELD_POSTBACK_TARGET))
+ $this->_postData=$postData;
+ }
+ public function getIsPostBack()
+ {
+ return $this->_postData!==null;
+ }
+ public function getIsCallback()
+ {
+ return $this->getIsPostBack() && $this->getRequest()->contains(self::FIELD_CALLBACK_TARGET);
+ }
+ public function saveState()
+ {
+ parent::saveState();
+ $this->setViewState('ControlsRequiringPostBack',$this->_controlsRegisteredForPostData,array());
+ }
+ public function loadState()
+ {
+ parent::loadState();
+ $this->_controlsRequiringPostData=$this->getViewState('ControlsRequiringPostBack',array());
+ }
+ protected function loadPageState()
+ {
+ $state=$this->getStatePersister()->load();
+ $this->loadStateRecursive($state,$this->getEnableViewState());
+ }
+ protected function savePageState()
+ {
+ $state=&$this->saveStateRecursive($this->getEnableViewState());
+ $this->getStatePersister()->save($state);
+ }
+ protected function isSystemPostField($field)
+ {
+ return isset(self::$_systemPostFields[$field]);
+ }
+ public function registerRequiresPostData($control)
+ {
+ $id=is_string($control)?$control:$control->getUniqueID();
+ $this->_controlsRegisteredForPostData[$id]=true;
+ $this->registerPostDataLoader($id);
+ $params=func_get_args();
+ foreach($this->getCachingStack() as $item)
+ $item->registerAction('Page','registerRequiresPostData',$id);
+ }
+ public function getPostBackEventTarget()
+ {
+ if($this->_postBackEventTarget===null && $this->_postData!==null)
+ {
+ $eventTarget=$this->_postData->itemAt(self::FIELD_POSTBACK_TARGET);
+ if(!empty($eventTarget))
+ $this->_postBackEventTarget=$this->findControl($eventTarget);
+ }
+ return $this->_postBackEventTarget;
+ }
+ public function setPostBackEventTarget(TControl $control)
+ {
+ $this->_postBackEventTarget=$control;
+ }
+ public function getPostBackEventParameter()
+ {
+ if($this->_postBackEventParameter===null && $this->_postData!==null)
+ {
+ if(($this->_postBackEventParameter=$this->_postData->itemAt(self::FIELD_POSTBACK_PARAMETER))===null)
+ $this->_postBackEventParameter='';
+ }
+ return $this->_postBackEventParameter;
+ }
+ public function setPostBackEventParameter($value)
+ {
+ $this->_postBackEventParameter=$value;
+ }
+ protected function processPostData($postData,$beforeLoad)
+ {
+ $this->_isLoadingPostData=true;
+ if($beforeLoad)
+ $this->_restPostData=new TMap;
+ foreach($postData as $key=>$value)
+ {
+ if($this->isSystemPostField($key))
+ continue;
+ else if($control=$this->findControl($key))
+ {
+ if($control instanceof IPostBackDataHandler)
+ {
+ if($control->loadPostData($key,$postData))
+ $this->_controlsPostDataChanged[]=$control;
+ }
+ else if($control instanceof IPostBackEventHandler &&
+ empty($this->_postData[self::FIELD_POSTBACK_TARGET]))
+ {
+ $this->_postData->add(self::FIELD_POSTBACK_TARGET,$key);
+ }
+ unset($this->_controlsRequiringPostData[$key]);
+ }
+ else if($beforeLoad)
+ $this->_restPostData->add($key,$value);
+ }
+ foreach($this->_controlsRequiringPostData as $key=>$value)
+ {
+ if($control=$this->findControl($key))
+ {
+ if($control instanceof IPostBackDataHandler)
+ {
+ if($control->loadPostData($key,$this->_postData))
+ $this->_controlsPostDataChanged[]=$control;
+ }
+ else
+ throw new TInvalidDataValueException('page_postbackcontrol_invalid',$key);
+ unset($this->_controlsRequiringPostData[$key]);
+ }
+ }
+ $this->_isLoadingPostData=false;
+ }
+ public function getIsLoadingPostData()
+ {
+ return $this->_isLoadingPostData;
+ }
+ private function raiseChangedEvents()
+ {
+ foreach($this->_controlsPostDataChanged as $control)
+ $control->raisePostDataChangedEvent();
+ }
+ private function raisePostBackEvent()
+ {
+ if(($postBackHandler=$this->getPostBackEventTarget())===null)
+ $this->validate();
+ else if($postBackHandler instanceof IPostBackEventHandler)
+ $postBackHandler->raisePostBackEvent($this->getPostBackEventParameter());
+ }
+ public function ensureRenderInForm($control)
+ {
+ if(!$this->getIsCallback() && !$this->_inFormRender)
+ throw new TConfigurationException('page_control_outofform',get_class($control),$control->getUniqueID());
+ }
+ public function beginFormRender($writer)
+ {
+ if($this->_formRendered)
+ throw new TConfigurationException('page_form_duplicated');
+ $this->_formRendered=true;
+ $this->_inFormRender=true;
+ $this->getClientScript()->registerHiddenField(self::FIELD_PAGESTATE,$this->getClientState());
+ }
+ public function endFormRender($writer)
+ {
+ if($this->_focus)
+ {
+ if(($this->_focus instanceof TControl) && $this->_focus->getVisible(true))
+ $focus=$this->_focus->getClientID();
+ else
+ $focus=$this->_focus;
+ $this->getClientScript()->registerFocusControl($focus);
+ }
+ else if($this->_postData && ($lastFocus=$this->_postData->itemAt(self::FIELD_LASTFOCUS))!==null)
+ $this->getClientScript()->registerFocusControl($lastFocus);
+ $this->_inFormRender=false;
+ }
+ public function setFocus($value)
+ {
+ $this->_focus=$value;
+ }
+ public function getClientSupportsJavaScript()
+ {
+ return $this->_enableJavaScript;
+ }
+ public function setClientSupportsJavaScript($value)
+ {
+ $this->_enableJavaScript=TPropertyValue::ensureBoolean($value);
+ }
+ public function getHead()
+ {
+ return $this->_head;
+ }
+ public function setHead(THead $value)
+ {
+ if($this->_head)
+ throw new TInvalidOperationException('page_head_duplicated');
+ $this->_head=$value;
+ if($this->_title!==null)
+ {
+ $this->_head->setTitle($this->_title);
+ $this->_title=null;
+ }
+ }
+ public function getTitle()
+ {
+ if($this->_head)
+ return $this->_head->getTitle();
+ else
+ return $this->_title===null ? '' : $this->_title;
+ }
+ public function setTitle($value)
+ {
+ if($this->_head)
+ $this->_head->setTitle($value);
+ else
+ $this->_title=$value;
+ }
+ public function getClientState()
+ {
+ return $this->_clientState;
+ }
+ public function setClientState($state)
+ {
+ $this->_clientState=$state;
+ }
+ public function getRequestClientState()
+ {
+ return $this->getRequest()->itemAt(self::FIELD_PAGESTATE);
+ }
+ public function getStatePersisterClass()
+ {
+ return $this->_statePersisterClass;
+ }
+ public function setStatePersisterClass($value)
+ {
+ $this->_statePersisterClass=$value;
+ }
+ public function getStatePersister()
+ {
+ if($this->_statePersister===null)
+ {
+ $this->_statePersister=Prado::createComponent($this->_statePersisterClass);
+ if(!($this->_statePersister instanceof IPageStatePersister))
+ throw new TInvalidDataTypeException('page_statepersister_invalid');
+ $this->_statePersister->setPage($this);
+ }
+ return $this->_statePersister;
+ }
+ public function getEnableStateValidation()
+ {
+ return $this->_enableStateValidation;
+ }
+ public function setEnableStateValidation($value)
+ {
+ $this->_enableStateValidation=TPropertyValue::ensureBoolean($value);
+ }
+ public function getEnableStateEncryption()
+ {
+ return $this->_enableStateEncryption;
+ }
+ public function setEnableStateEncryption($value)
+ {
+ $this->_enableStateEncryption=TPropertyValue::ensureBoolean($value);
+ }
+ public function getPagePath()
+ {
+ return $this->_pagePath;
+ }
+ public function setPagePath($value)
+ {
+ $this->_pagePath=$value;
+ }
+ public function registerCachingAction($context,$funcName,$funcParams)
+ {
+ if($this->_cachingStack)
+ {
+ foreach($this->_cachingStack as $cache)
+ $cache->registerAction($context,$funcName,$funcParams);
+ }
+ }
+ public function getCachingStack()
+ {
+ if(!$this->_cachingStack)
+ $this->_cachingStack=new TStack;
+ return $this->_cachingStack;
+ }
+}
+interface IPageStatePersister
+{
+ public function getPage();
+ public function setPage(TPage $page);
+ public function save($state);
+ public function load();
+}
+class TPageStateFormatter
+{
+ public static function serialize($page,$data)
+ {
+ $sm=$page->getApplication()->getSecurityManager();
+ if($page->getEnableStateValidation())
+ $str=$sm->hashData(Prado::serialize($data));
+ else
+ $str=Prado::serialize($data);
+ if(extension_loaded('zlib'))
+ $str=gzcompress($str);
+ if($page->getEnableStateEncryption())
+ $str=$sm->encrypt($str);
+ return base64_encode($str);
+ }
+ public static function unserialize($page,$data)
+ {
+ $str=base64_decode($data);
+ if($str==='')
+ return null;
+ if($str!==false)
+ {
+ $sm=$page->getApplication()->getSecurityManager();
+ if($page->getEnableStateEncryption())
+ $str=$sm->decrypt($str);
+ if(extension_loaded('zlib'))
+ $str=@gzuncompress($str);
+ if($page->getEnableStateValidation())
+ {
+ if(($str=$sm->validateData($str))!==false)
+ return Prado::unserialize($str);
+ }
+ else
+ return $str;
+ }
+ return null;
+ }
+}
+class TOutputCache extends TControl implements INamingContainer
+{
+ const CACHE_ID_PREFIX='prado:outputcache';
+ private $_cacheModuleID='';
+ private $_dataCached=false;
+ private $_cacheAvailable=false;
+ private $_cacheChecked=false;
+ private $_cacheKey=null;
+ private $_duration=60;
+ private $_cache=null;
+ private $_contents;
+ private $_state;
+ private $_actions=array();
+ private $_varyByParam='';
+ private $_keyPrefix='';
+ private $_varyBySession=false;
+ private $_cachePostBack=false;
+ private $_cacheTime=0;
+ public function getAllowChildControls()
+ {
+ $this->determineCacheability();
+ return !$this->_dataCached;
+ }
+ private function determineCacheability()
+ {
+ if(!$this->_cacheChecked)
+ {
+ $this->_cacheChecked=true;
+ if($this->_duration>0 && ($this->_cachePostBack || !$this->getPage()->getIsPostBack()))
+ {
+ if($this->_cacheModuleID!=='')
+ {
+ $this->_cache=$this->getApplication()->getModule($this->_cacheModuleID);
+ if(!($this->_cache instanceof ICache))
+ throw new TConfigurationException('outputcache_cachemoduleid_invalid',$this->_cacheModuleID);
+ }
+ else
+ $this->_cache=$this->getApplication()->getCache();
+ if($this->_cache!==null)
+ {
+ $this->_cacheAvailable=true;
+ $data=$this->_cache->get($this->getCacheKey());
+ if(is_array($data))
+ {
+ $param=new TOutputCacheCheckDependencyEventParameter;
+ $param->setCacheTime(isset($data[3])?$data[3]:0);
+ $this->onCheckDependency($param);
+ $this->_dataCached=$param->getIsValid();
+ }
+ else
+ $this->_dataCached=false;
+ if($this->_dataCached)
+ list($this->_contents,$this->_state,$this->_actions,$this->_cacheTime)=$data;
+ }
+ }
+ }
+ }
+ protected function initRecursive($namingContainer=null)
+ {
+ if($this->_cacheAvailable && !$this->_dataCached)
+ {
+ $stack=$this->getPage()->getCachingStack();
+ $stack->push($this);
+ parent::initRecursive($namingContainer);
+ $stack->pop();
+ }
+ else
+ parent::initRecursive($namingContainer);
+ }
+ protected function loadRecursive()
+ {
+ if($this->_cacheAvailable && !$this->_dataCached)
+ {
+ $stack=$this->getPage()->getCachingStack();
+ $stack->push($this);
+ parent::loadRecursive();
+ $stack->pop();
+ }
+ else
+ {
+ if($this->_dataCached)
+ $this->performActions();
+ parent::loadRecursive();
+ }
+ }
+ private function performActions()
+ {
+ $page=$this->getPage();
+ $cs=$page->getClientScript();
+ foreach($this->_actions as $action)
+ {
+ if($action[0]==='Page.ClientScript')
+ call_user_func_array(array($cs,$action[1]),$action[2]);
+ else if($action[0]==='Page')
+ call_user_func_array(array($page,$action[1]),$action[2]);
+ else
+ call_user_func_array(array($this->getSubProperty($action[0]),$action[1]),$action[2]);
+ }
+ }
+ protected function preRenderRecursive()
+ {
+ if($this->_cacheAvailable && !$this->_dataCached)
+ {
+ $stack=$this->getPage()->getCachingStack();
+ $stack->push($this);
+ parent::preRenderRecursive();
+ $stack->pop();
+ }
+ else
+ parent::preRenderRecursive();
+ }
+ protected function loadStateRecursive(&$state,$needViewState=true)
+ {
+ $st=unserialize($state);
+ parent::loadStateRecursive($st,$needViewState);
+ }
+ protected function &saveStateRecursive($needViewState=true)
+ {
+ if($this->_dataCached)
+ return $this->_state;
+ else
+ {
+ $st=parent::saveStateRecursive($needViewState);
+ $this->_state=serialize($st);
+ return $this->_state;
+ }
+ }
+ public function registerAction($context,$funcName,$funcParams)
+ {
+ $this->_actions[]=array($context,$funcName,$funcParams);
+ }
+ private function getCacheKey()
+ {
+ if($this->_cacheKey===null)
+ $this->_cacheKey=$this->calculateCacheKey();
+ return $this->_cacheKey;
+ }
+ protected function calculateCacheKey()
+ {
+ $key=$this->getBaseCacheKey();
+ if($this->_varyBySession)
+ $key.=$this->getSession()->getSessionID();
+ if($this->_varyByParam!=='')
+ {
+ $params=array();
+ $request=$this->getRequest();
+ foreach(explode(',',$this->_varyByParam) as $name)
+ {
+ $name=trim($name);
+ $params[$name]=$request->itemAt($name);
+ }
+ $key.=serialize($params);
+ }
+ $param=new TOutputCacheCalculateKeyEventParameter;
+ $this->onCalculateKey($param);
+ $key.=$param->getCacheKey();
+ return $key;
+ }
+ protected function getBaseCacheKey()
+ {
+ return self::CACHE_ID_PREFIX.$this->_keyPrefix.$this->getPage()->getPagePath().$this->getUniqueID();
+ }
+ public function getCacheModuleID()
+ {
+ return $this->_cacheModuleID;
+ }
+ public function setCacheModuleID($value)
+ {
+ $this->_cacheModuleID=$value;
+ }
+ public function setCacheKeyPrefix($value)
+ {
+ $this->_keyPrefix=$value;
+ }
+ public function getCacheTime()
+ {
+ return $this->_cacheTime;
+ }
+ protected function getCacheDependency()
+ {
+ return null;
+ }
+ public function getContentCached()
+ {
+ return $this->_dataCached;
+ }
+ public function getDuration()
+ {
+ return $this->_duration;
+ }
+ public function setDuration($value)
+ {
+ if(($value=TPropertyValue::ensureInteger($value))<0)
+ throw new TInvalidDataValueException('outputcache_duration_invalid',get_class($this));
+ $this->_duration=$value;
+ }
+ public function getVaryByParam()
+ {
+ return $this->_varyByParam;
+ }
+ public function setVaryByParam($value)
+ {
+ $this->_varyByParam=trim($value);
+ }
+ public function getVaryBySession()
+ {
+ return $this->_varyBySession;
+ }
+ public function setVaryBySession($value)
+ {
+ $this->_varyBySession=TPropertyValue::ensureBoolean($value);
+ }
+ public function getCachingPostBack()
+ {
+ return $this->_cachePostBack;
+ }
+ public function setCachingPostBack($value)
+ {
+ $this->_cachePostBack=TPropertyValue::ensureBoolean($value);
+ }
+ public function onCheckDependency($param)
+ {
+ $this->raiseEvent('OnCheckDependency',$this,$param);
+ }
+ public function onCalculateKey($param)
+ {
+ $this->raiseEvent('OnCalculateKey',$this,$param);
+ }
+ public function render($writer)
+ {
+ if($this->_dataCached)
+ $writer->write($this->_contents);
+ else if($this->_cacheAvailable)
+ {
+ $textWriter=new TTextWriter;
+ $stack=$this->getPage()->getCachingStack();
+ $stack->push($this);
+ parent::render(new THtmlWriter($textWriter));
+ $stack->pop();
+ $content=$textWriter->flush();
+ $data=array($content,$this->_state,$this->_actions,time());
+ $this->_cache->set($this->getCacheKey(),$data,$this->getDuration(),$this->getCacheDependency());
+ $writer->write($content);
+ }
+ else
+ parent::render($writer);
+ }
+}
+class TOutputCacheCheckDependencyEventParameter extends TEventParameter
+{
+ private $_isValid=true;
+ private $_cacheTime=0;
+ public function getIsValid()
+ {
+ return $this->_isValid;
+ }
+ public function setIsValid($value)
+ {
+ $this->_isValid=TPropertyValue::ensureBoolean($value);
+ }
+ public function getCacheTime()
+ {
+ return $this->_cacheTime;
+ }
+ public function setCacheTime($value)
+ {
+ $this->_cacheTime=TPropertyValue::ensureInteger($value);
+ }
+}
+class TOutputCacheCalculateKeyEventParameter extends TEventParameter
+{
+ private $_cacheKey='';
+ public function getCacheKey()
+ {
+ return $this->_cacheKey;
+ }
+ public function setCacheKey($value)
+ {
+ $this->_cacheKey=TPropertyValue::ensureString($value);
+ }
+}
+class TTemplateManager extends TModule
+{
+ const TEMPLATE_FILE_EXT='.tpl';
+ const TEMPLATE_CACHE_PREFIX='prado:template:';
+ public function init($config)
+ {
+ $this->getService()->setTemplateManager($this);
+ }
+ public function getTemplateByClassName($className)
+ {
+ $class=new ReflectionClass($className);
+ $tplFile=dirname($class->getFileName()).DIRECTORY_SEPARATOR.$className.self::TEMPLATE_FILE_EXT;
+ return $this->getTemplateByFileName($tplFile);
+ }
+ public function getTemplateByFileName($fileName)
+ {
+ if(($fileName=$this->getLocalizedTemplate($fileName))!==null)
+ {
+ if(($cache=$this->getApplication()->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,$timestamps)=$array;
+ if($this->getApplication()->getMode()===TApplicationMode::Performance)
+ return $template;
+ $cacheValid=true;
+ foreach($timestamps as $tplFile=>$timestamp)
+ {
+ if(!is_file($tplFile) || filemtime($tplFile)>$timestamp)
+ {
+ $cacheValid=false;
+ break;
+ }
+ }
+ if($cacheValid)
+ return $template;
+ }
+ $template=new TTemplate(file_get_contents($fileName),dirname($fileName),$fileName);
+ $includedFiles=$template->getIncludedFiles();
+ $timestamps=array();
+ $timestamps[$fileName]=filemtime($fileName);
+ foreach($includedFiles as $includedFile)
+ $timestamps[$includedFile]=filemtime($includedFile);
+ $cache->set(self::TEMPLATE_CACHE_PREFIX.$fileName,array($template,$timestamps));
+ return $template;
+ }
+ }
+ else
+ return null;
+ }
+ protected function getLocalizedTemplate($filename)
+ {
+ if(($app=$this->getApplication()->getGlobalization(false))===null)
+ return is_file($filename)?$filename:null;
+ foreach($app->getLocalizedResource($filename) as $file)
+ {
+ if(($file=realpath($file))!==false && is_file($file))
+ return $file;
+ }
+ return null;
+ }
+}
+class TTemplate extends TApplicationComponent implements ITemplate
+{
+ const REGEX_RULES='/<!--.*?--!>|<!---.*?--->|<\/?com:([\w\.]+)((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?"|\s*[\w\.]+\s*=\s*<%.*?%>)*)\s*\/?>|<\/?prop:([\w\.]+)\s*>|<%@\s*((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?")*)\s*%>|<%[%#~\/\\$=\\[](.*?)%>|<prop:([\w\.]+)((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?"|\s*[\w\.]+\s*=\s*<%.*?%>)*)\s*\/>/msS';
+ const CONFIG_DATABIND=0;
+ const CONFIG_EXPRESSION=1;
+ const CONFIG_ASSET=2;
+ const CONFIG_PARAMETER=3;
+ const CONFIG_LOCALIZATION=4;
+ const CONFIG_TEMPLATE=5;
+ private $_tpl=array();
+ private $_directive=array();
+ private $_contextPath;
+ private $_tplFile=null;
+ private $_startingLine=0;
+ private $_content;
+ private $_sourceTemplate=true;
+ private $_hashCode='';
+ private $_tplControl=null;
+ private $_includedFiles=array();
+ private $_includeAtLine=array();
+ private $_includeLines=array();
+ public function __construct($template,$contextPath,$tplFile=null,$startingLine=0,$sourceTemplate=true)
+ {
+ $this->_sourceTemplate=$sourceTemplate;
+ $this->_contextPath=$contextPath;
+ $this->_tplFile=$tplFile;
+ $this->_startingLine=$startingLine;
+ $this->_content=$template;
+ $this->_hashCode=md5($template);
+ $this->parse($template);
+ $this->_content=null;
+ }
+ public function getTemplateFile()
+ {
+ return $this->_tplFile;
+ }
+ public function getIsSourceTemplate()
+ {
+ return $this->_sourceTemplate;
+ }
+ public function getContextPath()
+ {
+ return $this->_contextPath;
+ }
+ public function getDirective()
+ {
+ return $this->_directive;
+ }
+ public function getHashCode()
+ {
+ return $this->_hashCode;
+ }
+ public function &getItems()
+ {
+ return $this->_tpl;
+ }
+ public function instantiateIn($tplControl,$parentControl=null)
+ {
+ $this->_tplControl=$tplControl;
+ if($parentControl===null)
+ $parentControl=$tplControl;
+ if(($page=$tplControl->getPage())===null)
+ $page=$this->getService()->getRequestedPage();
+ $controls=array();
+ $directChildren=array();
+ foreach($this->_tpl as $key=>$object)
+ {
+ if($object[0]===-1)
+ $parent=$parentControl;
+ else if(isset($controls[$object[0]]))
+ $parent=$controls[$object[0]];
+ else
+ continue;
+ if(isset($object[2]))
+ {
+ $component=Prado::createComponent($object[1]);
+ $properties=&$object[2];
+ if($component instanceof TControl)
+ {
+ if($component instanceof TOutputCache)
+ $component->setCacheKeyPrefix($this->_hashCode.$key);
+ $component->setTemplateControl($tplControl);
+ if(isset($properties['id']))
+ {
+ if(is_array($properties['id']))
+ $properties['id']=$component->evaluateExpression($properties['id'][1]);
+ $tplControl->registerObject($properties['id'],$component);
+ }
+ if(isset($properties['skinid']))
+ {
+ if(is_array($properties['skinid']))
+ $component->setSkinID($component->evaluateExpression($properties['skinid'][1]));
+ else
+ $component->setSkinID($properties['skinid']);
+ unset($properties['skinid']);
+ }
+ $component->trackViewState(false);
+ $component->applyStyleSheetSkin($page);
+ foreach($properties as $name=>$value)
+ $this->configureControl($component,$name,$value);
+ $component->trackViewState(true);
+ if($parent===$parentControl)
+ $directChildren[]=$component;
+ else
+ $component->createdOnTemplate($parent);
+ if($component->getAllowChildControls())
+ $controls[$key]=$component;
+ }
+ else if($component instanceof TComponent)
+ {
+ $controls[$key]=$component;
+ if(isset($properties['id']))
+ {
+ if(is_array($properties['id']))
+ $properties['id']=$component->evaluateExpression($properties['id'][1]);
+ $tplControl->registerObject($properties['id'],$component);
+ if(!$component->hasProperty('id'))
+ unset($properties['id']);
+ }
+ foreach($properties as $name=>$value)
+ $this->configureComponent($component,$name,$value);
+ if($parent===$parentControl)
+ $directChildren[]=$component;
+ else
+ $component->createdOnTemplate($parent);
+ }
+ }
+ else
+ {
+ if($object[1] instanceof TCompositeLiteral)
+ {
+ $o=clone $object[1];
+ $o->setContainer($tplControl);
+ if($parent===$parentControl)
+ $directChildren[]=$o;
+ else
+ $parent->addParsedObject($o);
+ }
+ else
+ {
+ if($parent===$parentControl)
+ $directChildren[]=$object[1];
+ else
+ $parent->addParsedObject($object[1]);
+ }
+ }
+ }
+ foreach($directChildren as $control)
+ {
+ if($control instanceof TComponent)
+ $control->createdOnTemplate($parentControl);
+ else
+ $parentControl->addParsedObject($control);
+ }
+ }
+ protected function configureControl($control,$name,$value)
+ {
+ if(strncasecmp($name,'on',2)===0)
+ $this->configureEvent($control,$name,$value,$control);
+ else if(($pos=strrpos($name,'.'))===false)
+ $this->configureProperty($control,$name,$value);
+ else
+ $this->configureSubProperty($control,$name,$value);
+ }
+ protected function configureComponent($component,$name,$value)
+ {
+ if(strpos($name,'.')===false)
+ $this->configureProperty($component,$name,$value);
+ else
+ $this->configureSubProperty($component,$name,$value);
+ }
+ protected function configureEvent($control,$name,$value,$contextControl)
+ {
+ if(strpos($value,'.')===false)
+ $control->attachEventHandler($name,array($contextControl,'TemplateControl.'.$value));
+ else
+ $control->attachEventHandler($name,array($contextControl,$value));
+ }
+ protected function configureProperty($component,$name,$value)
+ {
+ if(is_array($value))
+ {
+ switch($value[0])
+ {
+ case self::CONFIG_DATABIND:
+ $component->bindProperty($name,$value[1]);
+ break;
+ case self::CONFIG_EXPRESSION:
+ if($component instanceof TControl)
+ $component->autoBindProperty($name,$value[1]);
+ else
+ {
+ $setter='set'.$name;
+ $component->$setter($this->_tplControl->evaluateExpression($value[1]));
+ }
+ break;
+ case self::CONFIG_TEMPLATE:
+ $setter='set'.$name;
+ $component->$setter($value[1]);
+ break;
+ case self::CONFIG_ASSET:
+ $setter='set'.$name;
+ $url=$this->publishFilePath($this->_contextPath.DIRECTORY_SEPARATOR.$value[1]);
+ $component->$setter($url);
+ break;
+ case self::CONFIG_PARAMETER:
+ $setter='set'.$name;
+ $component->$setter($this->getApplication()->getParameters()->itemAt($value[1]));
+ break;
+ case self::CONFIG_LOCALIZATION:
+ $setter='set'.$name;
+ $component->$setter(Prado::localize($value[1]));
+ break;
+ default:
+ throw new TConfigurationException('template_tag_unexpected',$name,$value[1]);
+ break;
+ }
+ }
+ else
+ {
+ $setter='set'.$name;
+ $component->$setter($value);
+ }
+ }
+ protected function configureSubProperty($component,$name,$value)
+ {
+ if(is_array($value))
+ {
+ switch($value[0])
+ {
+ case self::CONFIG_DATABIND:
+ $component->bindProperty($name,$value[1]);
+ break;
+ case self::CONFIG_EXPRESSION:
+ if($component instanceof TControl)
+ $component->autoBindProperty($name,$value[1]);
+ else
+ $component->setSubProperty($name,$this->_tplControl->evaluateExpression($value[1]));
+ break;
+ case self::CONFIG_TEMPLATE:
+ $component->setSubProperty($name,$value[1]);
+ break;
+ case self::CONFIG_ASSET:
+ $url=$this->publishFilePath($this->_contextPath.DIRECTORY_SEPARATOR.$value[1]);
+ $component->setSubProperty($name,$url);
+ break;
+ case self::CONFIG_PARAMETER:
+ $component->setSubProperty($name,$this->getApplication()->getParameters()->itemAt($value[1]));
+ break;
+ case self::CONFIG_LOCALIZATION:
+ $component->setSubProperty($name,Prado::localize($value[1]));
+ break;
+ default:
+ throw new TConfigurationException('template_tag_unexpected',$name,$value[1]);
+ break;
+ }
+ }
+ else
+ $component->setSubProperty($name,$value);
+ }
+ protected function parse($input)
+ {
+ $input=$this->preprocess($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;
+ $matchEnd=0;
+ $c=0;
+ $this->_directive=null;
+ try
+ {
+ 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,'<com:')===0)
+ {
+ if($expectPropEnd)
+ continue;
+ if($matchStart>$textStart)
+ $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
+ $textStart=$matchEnd+1;
+ $type=$match[1][0];
+ $attributes=$this->parseAttributes($match[2][0],$match[2][1]);
+ $this->validateAttributes($type,$attributes);
+ $tpl[$c++]=array($container,$type,$attributes);
+ if($str[strlen($str)-2]!=='/')
+ {
+ $stack[] = $type;
+ $container=$c-1;
+ }
+ }
+ else if(strpos($str,'</com:')===0)
+ {
+ if($expectPropEnd)
+ continue;
+ if($matchStart>$textStart)
+ $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
+ $textStart=$matchEnd+1;
+ $type=$match[1][0];
+ if(empty($stack))
+ throw new TConfigurationException('template_closingtag_unexpected',"</com:$type>");
+ $name=array_pop($stack);
+ if($name!==$type)
+ {
+ $tag=$name[0]==='@' ? '</prop:'.substr($name,1).'>' : "</com:$name>";
+ throw new TConfigurationException('template_closingtag_expected',$tag);
+ }
+ $container=$tpl[$container][0];
+ }
+ else if(strpos($str,'<%@')===0)
+ {
+ if($expectPropEnd)
+ continue;
+ if($matchStart>$textStart)
+ $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
+ $textStart=$matchEnd+1;
+ if(isset($tpl[0]) || $this->_directive!==null)
+ throw new TConfigurationException('template_directive_nonunique');
+ $this->_directive=$this->parseAttributes($match[4][0],$match[4][1]);
+ }
+ else if(strpos($str,'<%')===0)
+ {
+ if($expectPropEnd)
+ continue;
+ if($matchStart>$textStart)
+ $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
+ $textStart=$matchEnd+1;
+ $literal=trim($match[5][0]);
+ if($str[2]==='=')
+ $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,$literal));
+ else if($str[2]==='%')
+ $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_STATEMENTS,$literal));
+ else if($str[2]==='#')
+ $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_DATABINDING,$literal));
+ else if($str[2]==='$')
+ $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"\$this->getApplication()->getParameters()->itemAt('$literal')"));
+ else if($str[2]==='~')
+ $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"\$this->publishFilePath('$this->_contextPath/$literal')"));
+ else if($str[2]==='/')
+ $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"dirname(\$this->getApplication()->getRequest()->getApplicationUrl()).'/$literal'"));
+ else if($str[2]==='[')
+ {
+ $literal=strtr(trim(substr($literal,0,strlen($literal)-1)),array("'"=>"\'","\\"=>"\\\\"));
+ $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"Prado::localize('$literal')"));
+ }
+ }
+ else if(strpos($str,'<prop:')===0)
+ {
+ if(strrpos($str,'/>')===strlen($str)-2)
+ {
+ if($expectPropEnd)
+ continue;
+ if($matchStart>$textStart)
+ $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
+ $textStart=$matchEnd+1;
+ $prop=strtolower($match[6][0]);
+ $attrs=$this->parseAttributes($match[7][0],$match[7][1]);
+ $attributes=array();
+ foreach($attrs as $name=>$value)
+ $attributes[$prop.'.'.$name]=$value;
+ $type=$tpl[$container][1];
+ $this->validateAttributes($type,$attributes);
+ foreach($attributes as $name=>$value)
+ {
+ if(isset($tpl[$container][2][$name]))
+ throw new TConfigurationException('template_property_duplicated',$name);
+ $tpl[$container][2][$name]=$value;
+ }
+ }
+ else
+ {
+ $prop=strtolower($match[3][0]);
+ $stack[] = '@'.$prop;
+ if(!$expectPropEnd)
+ {
+ if($matchStart>$textStart)
+ $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
+ $textStart=$matchEnd+1;
+ $expectPropEnd=true;
+ }
+ }
+ }
+ else if(strpos($str,'</prop:')===0)
+ {
+ $prop=strtolower($match[3][0]);
+ if(empty($stack))
+ throw new TConfigurationException('template_closingtag_unexpected',"</prop:$prop>");
+ $name=array_pop($stack);
+ if($name!=='@'.$prop)
+ {
+ $tag=$name[0]==='@' ? '</prop:'.substr($name,1).'>' : "</com:$name>";
+ throw new TConfigurationException('template_closingtag_expected',$tag);
+ }
+ if(($last=count($stack))<1 || $stack[$last-1][0]!=='@')
+ {
+ if($matchStart>$textStart)
+ {
+ $value=substr($input,$textStart,$matchStart-$textStart);
+ if(substr($prop,-8,8)==='template')
+ $value=$this->parseTemplateProperty($value,$textStart);
+ else
+ $value=$this->parseAttribute($value);
+ if($container>=0)
+ {
+ $type=$tpl[$container][1];
+ $this->validateAttributes($type,array($prop=>$value));
+ if(isset($tpl[$container][2][$prop]))
+ throw new TConfigurationException('template_property_duplicated',$prop);
+ $tpl[$container][2][$prop]=$value;
+ }
+ else
+ $this->_directive[$prop]=$value;
+ $textStart=$matchEnd+1;
+ }
+ $expectPropEnd=false;
+ }
+ }
+ else if(strpos($str,'<!--')===0)
+ {
+ if($expectPropEnd)
+ throw new TConfigurationException('template_comments_forbidden');
+ if($matchStart>$textStart)
+ $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
+ $textStart=$matchEnd+1;
+ }
+ else
+ throw new TConfigurationException('template_matching_unexpected',$match);
+ }
+ if(!empty($stack))
+ {
+ $name=array_pop($stack);
+ $tag=$name[0]==='@' ? '</prop:'.substr($name,1).'>' : "</com:$name>";
+ throw new TConfigurationException('template_closingtag_expected',$tag);
+ }
+ if($textStart<strlen($input))
+ $tpl[$c++]=array($container,substr($input,$textStart));
+ }
+ catch(Exception $e)
+ {
+ if(($e instanceof TException) && ($e instanceof TTemplateException))
+ throw $e;
+ if($matchEnd===0)
+ $line=$this->_startingLine+1;
+ else
+ $line=$this->_startingLine+count(explode("\n",substr($input,0,$matchEnd+1)));
+ $this->handleException($e,$line,$input);
+ }
+ if($this->_directive===null)
+ $this->_directive=array();
+ $objects=array();
+ $parent=null;
+ $merged=array();
+ foreach($tpl as $id=>$object)
+ {
+ if(isset($object[2]) || $object[0]!==$parent)
+ {
+ if($parent!==null)
+ {
+ if(count($merged[1])===1 && is_string($merged[1][0]))
+ $objects[$id-1]=array($merged[0],$merged[1][0]);
+ else
+ $objects[$id-1]=array($merged[0],new TCompositeLiteral($merged[1]));
+ }
+ if(isset($object[2]))
+ {
+ $parent=null;
+ $objects[$id]=$object;
+ }
+ else
+ {
+ $parent=$object[0];
+ $merged=array($parent,array($object[1]));
+ }
+ }
+ else
+ $merged[1][]=$object[1];
+ }
+ if($parent!==null)
+ {
+ if(count($merged[1])===1 && is_string($merged[1][0]))
+ $objects[$id]=array($merged[0],$merged[1][0]);
+ else
+ $objects[$id]=array($merged[0],new TCompositeLiteral($merged[1]));
+ }
+ $tpl=$objects;
+ return $objects;
+ }
+ protected function parseAttributes($str,$offset)
+ {
+ if($str==='')
+ return array();
+ $pattern='/([\w\.]+)\s*=\s*(\'.*?\'|".*?"|<%.*?%>)/msS';
+ $attributes=array();
+ $n=preg_match_all($pattern,$str,$matches,PREG_SET_ORDER|PREG_OFFSET_CAPTURE);
+ for($i=0;$i<$n;++$i)
+ {
+ $match=&$matches[$i];
+ $name=strtolower($match[1][0]);
+ if(isset($attributes[$name]))
+ throw new TConfigurationException('template_property_duplicated',$name);
+ $value=$match[2][0];
+ if(substr($name,-8,8)==='template')
+ {
+ if($value[0]==='\'' || $value[0]==='"')
+ $attributes[$name]=$this->parseTemplateProperty(substr($value,1,strlen($value)-2),$match[2][1]+1);
+ else
+ $attributes[$name]=$this->parseTemplateProperty($value,$match[2][1]);
+ }
+ else
+ {
+ if($value[0]==='\'' || $value[0]==='"')
+ $attributes[$name]=$this->parseAttribute(substr($value,1,strlen($value)-2));
+ else
+ $attributes[$name]=$this->parseAttribute($value);
+ }
+ }
+ return $attributes;
+ }
+ protected function parseTemplateProperty($content,$offset)
+ {
+ $line=$this->_startingLine+count(explode("\n",substr($this->_content,0,$offset)))-1;
+ return array(self::CONFIG_TEMPLATE,new TTemplate($content,$this->_contextPath,$this->_tplFile,$line,false));
+ }
+ protected function parseAttribute($value)
+ {
+ if(($n=preg_match_all('/<%[#=].*?%>/msS',$value,$matches,PREG_OFFSET_CAPTURE))>0)
+ {
+ $isDataBind=false;
+ $textStart=0;
+ $expr='';
+ for($i=0;$i<$n;++$i)
+ {
+ $match=$matches[0][$i];
+ $token=$match[0];
+ $offset=$match[1];
+ $length=strlen($token);
+ if($token[2]==='#')
+ $isDataBind=true;
+ if($offset>$textStart)
+ $expr.=".'".strtr(substr($value,$textStart,$offset-$textStart),array("'"=>"\\'","\\"=>"\\\\"))."'";
+ $expr.='.('.substr($token,3,$length-5).')';
+ $textStart=$offset+$length;
+ }
+ $length=strlen($value);
+ if($length>$textStart)
+ $expr.=".'".strtr(substr($value,$textStart,$length-$textStart),array("'"=>"\\'","\\"=>"\\\\"))."'";
+ if($isDataBind)
+ return array(self::CONFIG_DATABIND,ltrim($expr,'.'));
+ else
+ return array(self::CONFIG_EXPRESSION,ltrim($expr,'.'));
+ }
+ else if(preg_match('/\\s*(<%~.*?%>|<%\\$.*?%>|<%\\[.*?\\]%>)\\s*/msS',$value,$matches) && $matches[0]===$value)
+ {
+ $value=$matches[1];
+ if($value[2]==='~')
+ return array(self::CONFIG_ASSET,trim(substr($value,3,strlen($value)-5)));
+ else if($value[2]==='[')
+ return array(self::CONFIG_LOCALIZATION,trim(substr($value,3,strlen($value)-6)));
+ else if($value[2]==='$')
+ return array(self::CONFIG_PARAMETER,trim(substr($value,3,strlen($value)-5)));
+ }
+ else
+ return $value;
+ }
+ protected function validateAttributes($type,$attributes)
+ {
+ Prado::using($type);
+ if(($pos=strrpos($type,'.'))!==false)
+ $className=substr($type,$pos+1);
+ else
+ $className=$type;
+ $class=new TReflectionClass($className);
+ if(is_subclass_of($className,'TControl') || $className==='TControl')
+ {
+ foreach($attributes as $name=>$att)
+ {
+ if(($pos=strpos($name,'.'))!==false)
+ {
+ $subname=substr($name,0,$pos);
+ if(!$class->hasMethod('get'.$subname))
+ throw new TConfigurationException('template_property_unknown',$type,$subname);
+ }
+ else if(strncasecmp($name,'on',2)===0)
+ {
+ if(!$class->hasMethod($name))
+ throw new TConfigurationException('template_event_unknown',$type,$name);
+ else if(!is_string($att))
+ throw new TConfigurationException('template_eventhandler_invalid',$type,$name);
+ }
+ else
+ {
+ if(!$class->hasMethod('set'.$name))
+ {
+ if($class->hasMethod('get'.$name))
+ throw new TConfigurationException('template_property_readonly',$type,$name);
+ else
+ throw new TConfigurationException('template_property_unknown',$type,$name);
+ }
+ else if(is_array($att) && $att[0]!==self::CONFIG_EXPRESSION)
+ {
+ if(strcasecmp($name,'id')===0)
+ throw new TConfigurationException('template_controlid_invalid',$type);
+ else if(strcasecmp($name,'skinid')===0)
+ throw new TConfigurationException('template_controlskinid_invalid',$type);
+ }
+ }
+ }
+ }
+ else if(is_subclass_of($className,'TComponent') || $className==='TComponent')
+ {
+ foreach($attributes as $name=>$att)
+ {
+ if(is_array($att) && ($att[0]===self::CONFIG_DATABIND))
+ throw new TConfigurationException('template_databind_forbidden',$type,$name);
+ if(($pos=strpos($name,'.'))!==false)
+ {
+ $subname=substr($name,0,$pos);
+ if(!$class->hasMethod('get'.$subname))
+ throw new TConfigurationException('template_property_unknown',$type,$subname);
+ }
+ else if(strncasecmp($name,'on',2)===0)
+ throw new TConfigurationException('template_event_forbidden',$type,$name);
+ else
+ {
+ if(strcasecmp($name,'id')!==0 && !$class->hasMethod('set'.$name))
+ {
+ if($class->hasMethod('get'.$name))
+ throw new TConfigurationException('template_property_readonly',$type,$name);
+ else
+ throw new TConfigurationException('template_property_unknown',$type,$name);
+ }
+ }
+ }
+ }
+ else
+ throw new TConfigurationException('template_component_required',$type);
+ }
+ public function getIncludedFiles()
+ {
+ return $this->_includedFiles;
+ }
+ protected function handleException($e,$line,$input=null)
+ {
+ $srcFile=$this->_tplFile;
+ if(($n=count($this->_includedFiles))>0)
+ {
+ for($i=$n-1;$i>=0;--$i)
+ {
+ if($this->_includeAtLine[$i]<=$line)
+ {
+ if($line<$this->_includeAtLine[$i]+$this->_includeLines[$i])
+ {
+ $line=$line-$this->_includeAtLine[$i]+1;
+ $srcFile=$this->_includedFiles[$i];
+ break;
+ }
+ else
+ $line=$line-$this->_includeLines[$i]+1;
+ }
+ }
+ }
+ $exception=new TTemplateException('template_format_invalid',$e->getMessage());
+ $exception->setLineNumber($line);
+ if(!empty($srcFile))
+ $exception->setTemplateFile($srcFile);
+ else
+ $exception->setTemplateSource($input);
+ throw $exception;
+ }
+ protected function preprocess($input)
+ {
+ if($n=preg_match_all('/<%include(.*?)%>/',$input,$matches,PREG_SET_ORDER|PREG_OFFSET_CAPTURE))
+ {
+ for($i=0;$i<$n;++$i)
+ {
+ $filePath=Prado::getPathOfNamespace(trim($matches[$i][1][0]),TTemplateManager::TEMPLATE_FILE_EXT);
+ if($filePath!==null && is_file($filePath))
+ $this->_includedFiles[]=$filePath;
+ else
+ {
+ $errorLine=count(explode("\n",substr($input,0,$matches[$i][0][1]+1)));
+ $this->handleException(new TConfigurationException('template_include_invalid',trim($matches[$i][1][0])),$errorLine,$input);
+ }
+ }
+ $base=0;
+ for($i=0;$i<$n;++$i)
+ {
+ $ext=file_get_contents($this->_includedFiles[$i]);
+ $length=strlen($matches[$i][0][0]);
+ $offset=$base+$matches[$i][0][1];
+ $this->_includeAtLine[$i]=count(explode("\n",substr($input,0,$offset)));
+ $this->_includeLines[$i]=count(explode("\n",$ext));
+ $input=substr_replace($input,$ext,$offset,$length);
+ $base+=strlen($ext)-$length;
+ }
+ }
+ return $input;
+ }
+}
+class TThemeManager extends TModule
+{
+ const DEFAULT_BASEPATH='themes';
+ private $_initialized=false;
+ private $_basePath=null;
+ private $_baseUrl=null;
+ public function init($config)
+ {
+ $this->_initialized=true;
+ $service=$this->getService();
+ if($service instanceof TPageService)
+ $service->setThemeManager($this);
+ else
+ throw new TConfigurationException('thememanager_service_unavailable');
+ }
+ public function getTheme($name)
+ {
+ $themePath=$this->getBasePath().DIRECTORY_SEPARATOR.$name;
+ $themeUrl=rtrim($this->getBaseUrl(),'/').'/'.$name;
+ return new TTheme($themePath,$themeUrl);
+ }
+ public function getAvailableThemes()
+ {
+ $themes=array();
+ $basePath=$this->getBasePath();
+ $folder=@opendir($basePath);
+ while($file=@readdir($folder))
+ {
+ if($file!=='.' && $file!=='..' && $file!=='.svn' && is_dir($basePath.DIRECTORY_SEPARATOR.$file))
+ $themes[]=$file;
+ }
+ closedir($folder);
+ return $themes;
+ }
+ public function getBasePath()
+ {
+ if($this->_basePath===null)
+ {
+ $this->_basePath=dirname($this->getRequest()->getApplicationFilePath()).DIRECTORY_SEPARATOR.self::DEFAULT_BASEPATH;
+ if(($basePath=realpath($this->_basePath))===false || !is_dir($basePath))
+ throw new TConfigurationException('thememanager_basepath_invalid2',$this->_basePath);
+ $this->_basePath=$basePath;
+ }
+ return $this->_basePath;
+ }
+ public function setBasePath($value)
+ {
+ if($this->_initialized)
+ throw new TInvalidOperationException('thememanager_basepath_unchangeable');
+ else
+ {
+ $this->_basePath=Prado::getPathOfNamespace($value);
+ if($this->_basePath===null || !is_dir($this->_basePath))
+ throw new TInvalidDataValueException('thememanager_basepath_invalid',$value);
+ }
+ }
+ public function getBaseUrl()
+ {
+ if($this->_baseUrl===null)
+ {
+ $appPath=dirname($this->getRequest()->getApplicationFilePath());
+ $basePath=$this->getBasePath();
+ if(strpos($basePath,$appPath)===false)
+ throw new TConfigurationException('thememanager_baseurl_required');
+ $appUrl=rtrim(dirname($this->getRequest()->getApplicationUrl()),'/\\');
+ $this->_baseUrl=$appUrl.strtr(substr($basePath,strlen($appPath)),'\\','/');
+ }
+ return $this->_baseUrl;
+ }
+ public function setBaseUrl($value)
+ {
+ $this->_baseUrl=rtrim($value,'/');
+ }
+}
+class TTheme extends TApplicationComponent implements ITheme
+{
+ const THEME_CACHE_PREFIX='prado:theme:';
+ const SKIN_FILE_EXT='.skin';
+ private $_themePath;
+ private $_themeUrl;
+ private $_skins=null;
+ private $_name='';
+ private $_cssFiles=array();
+ private $_jsFiles=array();
+ public function __construct($themePath,$themeUrl)
+ {
+ $this->_themeUrl=$themeUrl;
+ $this->_themePath=realpath($themePath);
+ $this->_name=basename($themePath);
+ $cacheValid=false;
+ if(($cache=$this->getApplication()->getCache())!==null)
+ {
+ $array=$cache->get(self::THEME_CACHE_PREFIX.$themePath);
+ if(is_array($array))
+ {
+ list($skins,$cssFiles,$jsFiles,$timestamp)=$array;
+ if($this->getApplication()->getMode()!==TApplicationMode::Performance)
+ {
+ if(($dir=opendir($themePath))===false)
+ throw new TIOException('theme_path_inexistent',$themePath);
+ $cacheValid=true;
+ while(($file=readdir($dir))!==false)
+ {
+ if($file==='.' || $file==='..')
+ continue;
+ else if(basename($file,'.css')!==$file)
+ $this->_cssFiles[]=$themeUrl.'/'.$file;
+ else if(basename($file,'.js')!==$file)
+ $this->_jsFiles[]=$themeUrl.'/'.$file;
+ else if(basename($file,self::SKIN_FILE_EXT)!==$file && filemtime($themePath.DIRECTORY_SEPARATOR.$file)>$timestamp)
+ {
+ $cacheValid=false;
+ break;
+ }
+ }
+ closedir($dir);
+ if($cacheValid)
+ $this->_skins=$skins;
+ }
+ else
+ {
+ $cacheValid=true;
+ $this->_cssFiles=$cssFiles;
+ $this->_jsFiles=$jsFiles;
+ $this->_skins=$skins;
+ }
+ }
+ }
+ if(!$cacheValid)
+ {
+ $this->_cssFiles=array();
+ $this->_jsFiles=array();
+ $this->_skins=array();
+ if(($dir=opendir($themePath))===false)
+ throw new TIOException('theme_path_inexistent',$themePath);
+ while(($file=readdir($dir))!==false)
+ {
+ if($file==='.' || $file==='..')
+ continue;
+ else if(basename($file,'.css')!==$file)
+ $this->_cssFiles[]=$themeUrl.'/'.$file;
+ else if(basename($file,'.js')!==$file)
+ $this->_jsFiles[]=$themeUrl.'/'.$file;
+ else if(basename($file,self::SKIN_FILE_EXT)!==$file)
+ {
+ $template=new TTemplate(file_get_contents($themePath.'/'.$file),$themePath,$themePath.'/'.$file);
+ foreach($template->getItems() as $skin)
+ {
+ if(!isset($skin[2]))
+ continue;
+ else if($skin[0]!==-1)
+ throw new TConfigurationException('theme_control_nested',$skin[1],dirname($themePath));
+ $type=$skin[1];
+ $id=isset($skin[2]['skinid'])?$skin[2]['skinid']:0;
+ unset($skin[2]['skinid']);
+ if(isset($this->_skins[$type][$id]))
+ throw new TConfigurationException('theme_skinid_duplicated',$type,$id,dirname($themePath));
+ $this->_skins[$type][$id]=$skin[2];
+ }
+ }
+ }
+ closedir($dir);
+ sort($this->_cssFiles);
+ sort($this->_jsFiles);
+ if($cache!==null)
+ $cache->set(self::THEME_CACHE_PREFIX.$themePath,array($this->_skins,$this->_cssFiles,$this->_jsFiles,time()));
+ }
+ }
+ public function getName()
+ {
+ return $this->_name;
+ }
+ protected function setName($value)
+ {
+ $this->_name = $value;
+ }
+ public function getBaseUrl()
+ {
+ return $this->_themeUrl;
+ }
+ protected function setBaseUrl($value)
+ {
+ $this->_themeUrl=rtrim($value,'/');
+ }
+ public function getBasePath()
+ {
+ return $this->_themePath;
+ }
+ protected function setBasePath($value)
+ {
+ $this->_themePath=$value;
+ }
+ public function getSkins()
+ {
+ return $this->_skins;
+ }
+ protected function setSkins($value)
+ {
+ $this->_skins = $value;
+ }
+ public function applySkin($control)
+ {
+ $type=get_class($control);
+ if(($id=$control->getSkinID())==='')
+ $id=0;
+ if(isset($this->_skins[$type][$id]))
+ {
+ foreach($this->_skins[$type][$id] as $name=>$value)
+ {
+ if(is_array($value))
+ {
+ switch($value[0])
+ {
+ case TTemplate::CONFIG_EXPRESSION:
+ $value=$this->evaluateExpression($value[1]);
+ break;
+ case TTemplate::CONFIG_ASSET:
+ $value=$this->_themeUrl.'/'.ltrim($value[1],'/');
+ break;
+ case TTemplate::CONFIG_DATABIND:
+ $control->bindProperty($name,$value[1]);
+ break;
+ case TTemplate::CONFIG_PARAMETER:
+ $control->setSubProperty($name,$this->getApplication()->getParameters()->itemAt($value[1]));
+ break;
+ case TTemplate::CONFIG_TEMPLATE:
+ $control->setSubProperty($name,$value[1]);
+ break;
+ case TTemplate::CONFIG_LOCALIZATION:
+ $control->setSubProperty($name,Prado::localize($value[1]));
+ break;
+ default:
+ throw new TConfigurationException('theme_tag_unexpected',$name,$value[0]);
+ break;
+ }
+ }
+ if(!is_array($value))
+ {
+ if(strpos($name,'.')===false)
+ {
+ if($control->hasProperty($name))
+ {
+ if($control->canSetProperty($name))
+ {
+ $setter='set'.$name;
+ $control->$setter($value);
+ }
+ else
+ throw new TConfigurationException('theme_property_readonly',$type,$name);
+ }
+ else
+ throw new TConfigurationException('theme_property_undefined',$type,$name);
+ }
+ else
+ $control->setSubProperty($name,$value);
+ }
+ }
+ return true;
+ }
+ else
+ return false;
+ }
+ public function getStyleSheetFiles()
+ {
+ return $this->_cssFiles;
+ }
+ protected function setStyleSheetFiles($value)
+ {
+ $this->_cssFiles=$value;
+ }
+ public function getJavaScriptFiles()
+ {
+ return $this->_jsFiles;
+ }
+ protected function setJavaScriptFiles($value)
+ {
+ $this->_jsFiles=$value;
+ }
+}
+class TPageService extends TService
+{
+ const CONFIG_FILE='config.xml';
+ const DEFAULT_BASEPATH='pages';
+ const CONFIG_CACHE_PREFIX='prado:pageservice:';
+ const PAGE_FILE_EXT='.page';
+ private $_basePath=null;
+ private $_basePageClass='TPage';
+ private $_defaultPage='Home';
+ private $_pagePath=null;
+ private $_page=null;
+ private $_properties=array();
+ private $_initialized=false;
+ private $_themeManager=null;
+ private $_templateManager=null;
+ public function init($config)
+ {
+ $pageConfig=$this->loadPageConfig($config);
+ $this->initPageContext($pageConfig);
+ $this->_initialized=true;
+ }
+ protected function initPageContext($pageConfig)
+ {
+ $application=$this->getApplication();
+ foreach($pageConfig->getApplicationConfigurations() as $appConfig)
+ $application->applyConfiguration($appConfig);
+ $this->applyConfiguration($pageConfig);
+ }
+ protected function applyConfiguration($config)
+ {
+ $this->_properties=array_merge($this->_properties, $config->getProperties());
+ $this->getApplication()->getAuthorizationRules()->mergeWith($config->getRules());
+ $pagePath=$this->getRequestedPagePath();
+ 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($pagePath);
+ $c->loadFromFile($path,$configPagePath);
+ $this->applyConfiguration($c);
+ }
+ }
+ }
+ protected function determineRequestedPagePath()
+ {
+ $pagePath=$this->getRequest()->getServiceParameter();
+ if(empty($pagePath))
+ $pagePath=$this->getDefaultPage();
+ return $pagePath;
+ }
+ protected function loadPageConfig($config)
+ {
+ $application=$this->getApplication();
+ $pagePath=$this->getRequestedPagePath();
+ if(($cache=$application->getCache())===null)
+ {
+ $pageConfig=new TPageConfiguration($pagePath);
+ if($config!==null)
+ $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath(),'');
+ $pageConfig->loadFromFiles($this->getBasePath());
+ }
+ else
+ {
+ $configCached=true;
+ $currentTimestamp=array();
+ $arr=$cache->get(self::CONFIG_CACHE_PREFIX.$this->getID().$pagePath);
+ if(is_array($arr))
+ {
+ list($pageConfig,$timestamps)=$arr;
+ if($application->getMode()!==TApplicationMode::Performance)
+ {
+ foreach($timestamps as $fileName=>$timestamp)
+ {
+ if($fileName===0)
+ {
+ $appConfigFile=$application->getConfigurationFile();
+ $currentTimestamp[0]=$appConfigFile===null?0:@filemtime($appConfigFile);
+ if($currentTimestamp[0]>$timestamp || ($timestamp>0 && !$currentTimestamp[0]))
+ $configCached=false;
+ }
+ else
+ {
+ $currentTimestamp[$fileName]=@filemtime($fileName);
+ if($currentTimestamp[$fileName]>$timestamp || ($timestamp>0 && !$currentTimestamp[$fileName]))
+ $configCached=false;
+ }
+ }
+ }
+ }
+ else
+ {
+ $configCached=false;
+ $paths=explode('.',$pagePath);
+ $configPath=$this->getBasePath();
+ foreach($paths as $path)
+ {
+ $configFile=$configPath.DIRECTORY_SEPARATOR.self::CONFIG_FILE;
+ $currentTimestamp[$configFile]=@filemtime($configFile);
+ $configPath.=DIRECTORY_SEPARATOR.$path;
+ }
+ $appConfigFile=$application->getConfigurationFile();
+ $currentTimestamp[0]=$appConfigFile===null?0:@filemtime($appConfigFile);
+ }
+ if(!$configCached)
+ {
+ $pageConfig=new TPageConfiguration($pagePath);
+ if($config!==null)
+ $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath(),'');
+ $pageConfig->loadFromFiles($this->getBasePath());
+ $cache->set(self::CONFIG_CACHE_PREFIX.$this->getID().$pagePath,array($pageConfig,$currentTimestamp));
+ }
+ }
+ return $pageConfig;
+ }
+ public function getTemplateManager()
+ {
+ if(!$this->_templateManager)
+ {
+ $this->_templateManager=new TTemplateManager;
+ $this->_templateManager->init(null);
+ }
+ return $this->_templateManager;
+ }
+ public function setTemplateManager(TTemplateManager $value)
+ {
+ $this->_templateManager=$value;
+ }
+ public function getThemeManager()
+ {
+ if(!$this->_themeManager)
+ {
+ $this->_themeManager=new TThemeManager;
+ $this->_themeManager->init(null);
+ }
+ return $this->_themeManager;
+ }
+ public function setThemeManager(TThemeManager $value)
+ {
+ $this->_themeManager=$value;
+ }
+ public function getRequestedPagePath()
+ {
+ if($this->_pagePath===null)
+ {
+ $this->_pagePath=strtr($this->determineRequestedPagePath(),'/\\','..');
+ if(empty($this->_pagePath))
+ throw new THttpException(404,'pageservice_page_required');
+ }
+ return $this->_pagePath;
+ }
+ public function getRequestedPage()
+ {
+ return $this->_page;
+ }
+ public function getDefaultPage()
+ {
+ return $this->_defaultPage;
+ }
+ public function setDefaultPage($value)
+ {
+ if($this->_initialized)
+ throw new TInvalidOperationException('pageservice_defaultpage_unchangeable');
+ else
+ $this->_defaultPage=$value;
+ }
+ public function getDefaultPageUrl()
+ {
+ return $this->constructUrl($this->getDefaultPage());
+ }
+ public function getBasePath()
+ {
+ if($this->_basePath===null)
+ {
+ $basePath=$this->getApplication()->getBasePath().DIRECTORY_SEPARATOR.self::DEFAULT_BASEPATH;
+ if(($this->_basePath=realpath($basePath))===false || !is_dir($this->_basePath))
+ throw new TConfigurationException('pageservice_basepath_invalid',$basePath);
+ }
+ return $this->_basePath;
+ }
+ public function setBasePath($value)
+ {
+ if($this->_initialized)
+ throw new TInvalidOperationException('pageservice_basepath_unchangeable');
+ else if(($path=Prado::getPathOfNamespace($value))===null || !is_dir($path))
+ throw new TConfigurationException('pageservice_basepath_invalid',$value);
+ $this->_basePath=realpath($path);
+ }
+ public function setBasePageClass($value)
+ {
+ $this->_basePageClass=$value;
+ }
+ public function getBasePageClass()
+ {
+ return $this->_basePageClass;
+ }
+ public function run()
+ {
+ $this->_page=$this->createPage($this->getRequestedPagePath());
+ $this->runPage($this->_page,$this->_properties);
+ }
+ protected function createPage($pagePath)
+ {
+ $path=$this->getBasePath().DIRECTORY_SEPARATOR.strtr($pagePath,'.',DIRECTORY_SEPARATOR);
+ $hasTemplateFile=is_file($path.self::PAGE_FILE_EXT);
+ $hasClassFile=is_file($path.Prado::CLASS_FILE_EXT);
+ if(!$hasTemplateFile && !$hasClassFile)
+ throw new THttpException(404,'pageservice_page_unknown',$pagePath);
+ if($hasClassFile)
+ {
+ $className=basename($path);
+ if(!class_exists($className,false))
+ include_once($path.Prado::CLASS_FILE_EXT);
+ }
+ else
+ {
+ $className=$this->getBasePageClass();
+ Prado::using($className);
+ if(($pos=strrpos($className,'.'))!==false)
+ $className=substr($className,$pos+1);
+ }
+ if(!class_exists($className,false) || ($className!=='TPage' && !is_subclass_of($className,'TPage')))
+ throw new THttpException(404,'pageservice_page_unknown',$pagePath);
+ $page=new $className;
+ $page->setPagePath($pagePath);
+ if($hasTemplateFile)
+ $page->setTemplate($this->getTemplateManager()->getTemplateByFileName($path.self::PAGE_FILE_EXT));
+ return $page;
+ }
+ protected function runPage($page,$properties)
+ {
+ foreach($properties as $name=>$value)
+ $page->setSubProperty($name,$value);
+ $page->run($this->getResponse()->createHtmlWriter());
+ }
+ public function constructUrl($pagePath,$getParams=null,$encodeAmpersand=true,$encodeGetItems=true)
+ {
+ return $this->getRequest()->constructUrl($this->getID(),$pagePath,$getParams,$encodeAmpersand,$encodeGetItems);
+ }
+}
+class TPageConfiguration extends TComponent
+{
+ private $_appConfigs=array();
+ private $_properties=array();
+ private $_rules=array();
+ private $_includes=array();
+ private $_pagePath='';
+ public function __construct($pagePath)
+ {
+ $this->_pagePath=$pagePath;
+ }
+ public function getExternalConfigurations()
+ {
+ return $this->_includes;
+ }
+ public function getProperties()
+ {
+ return $this->_properties;
+ }
+ public function getRules()
+ {
+ return $this->_rules;
+ }
+ public function getApplicationConfigurations()
+ {
+ return $this->_appConfigs;
+ }
+ public function loadFromFiles($basePath)
+ {
+ $paths=explode('.',$this->_pagePath);
+ $page=array_pop($paths);
+ $path=$basePath;
+ $configPagePath='';
+ foreach($paths as $p)
+ {
+ $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,$configPagePath);
+ $this->_rules=new TAuthorizationRuleCollection($this->_rules);
+ }
+ public function loadFromFile($fname,$configPagePath)
+ {
+ if(empty($fname) || !is_file($fname))
+ return;
+ $dom=new TXmlDocument;
+ if($dom->loadFromFile($fname))
+ $this->loadFromXml($dom,dirname($fname),$configPagePath);
+ else
+ throw new TConfigurationException('pageserviceconf_file_invalid',$fname);
+ }
+ public function loadFromXml($dom,$configPath,$configPagePath)
+ {
+ $this->loadApplicationConfigurationFromXml($dom,$configPath);
+ $this->loadPageConfigurationFromXml($dom,$configPath,$configPagePath);
+ }
+ public function loadApplicationConfigurationFromXml($dom,$configPath)
+ {
+ $appConfig=new TApplicationConfiguration;
+ $appConfig->loadFromXml($dom,$configPath);
+ $this->_appConfigs[]=$appConfig;
+ }
+ public function loadPageConfigurationFromXml($dom,$configPath,$configPagePath)
+ {
+ if(($authorizationNode=$dom->getElementByTagName('authorization'))!==null)
+ {
+ $rules=array();
+ foreach($authorizationNode->getElements() as $node)
+ {
+ $patterns=$node->getAttribute('pages');
+ $ruleApplies=false;
+ if(empty($patterns) || trim($patterns)==='*')
+ $ruleApplies=true;
+ else
+ {
+ foreach(explode(',',$patterns) as $pattern)
+ {
+ if(($pattern=trim($pattern))!=='')
+ {
+ if($configPagePath!=='')
+ $pattern=$configPagePath.'.'.$pattern;
+ if(strcasecmp($pattern,$this->_pagePath)===0)
+ {
+ $ruleApplies=true;
+ break;
+ }
+ if($pattern[strlen($pattern)-1]==='*')
+ {
+ if(strncasecmp($this->_pagePath,$pattern,strlen($pattern)-1)===0)
+ {
+ $ruleApplies=true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if($ruleApplies)
+ $rules[]=new TAuthorizationRule($node->getTagName(),$node->getAttribute('users'),$node->getAttribute('roles'),$node->getAttribute('verb'),$node->getAttribute('ips'));
+ }
+ $this->_rules=array_merge($rules,$this->_rules);
+ }
+ if(($pagesNode=$dom->getElementByTagName('pages'))!==null)
+ {
+ $this->_properties=array_merge($this->_properties,$pagesNode->getAttributes()->toArray());
+ foreach($pagesNode->getElementsByTagName('page') as $node)
+ {
+ $properties=$node->getAttributes();
+ $id=$properties->remove('id');
+ if(empty($id))
+ throw new TConfigurationException('pageserviceconf_page_invalid',$configPath);
+ $matching=false;
+ $id=($configPagePath==='')?$id:$configPagePath.'.'.$id;
+ if(strcasecmp($id,$this->_pagePath)===0)
+ $matching=true;
+ else if($id[strlen($id)-1]==='*')
+ $matching=strncasecmp($this->_pagePath,$id,strlen($id)-1)===0;
+ if($matching)
+ $this->_properties=array_merge($this->_properties,$properties->toArray());
+ }
+ }
+ foreach($dom->getElementsByTagName('include') as $node)
+ {
+ if(($when=$node->getAttribute('when'))===null)
+ $when=true;
+ if(($filePath=$node->getAttribute('file'))===null)
+ throw new TConfigurationException('pageserviceconf_includefile_required');
+ if(isset($this->_includes[$filePath]))
+ $this->_includes[$filePath]=array($configPagePath,'('.$this->_includes[$filePath][1].') || ('.$when.')');
+ else
+ $this->_includes[$filePath]=array($configPagePath,$when);
+ }
+ }
+}
+class TAssetManager extends TModule
+{
+ const DEFAULT_BASEPATH='assets';
+ private $_basePath=null;
+ private $_baseUrl=null;
+ private $_checkTimestamp=false;
+ private $_application;
+ private $_published=array();
+ private $_initialized=false;
+ public function init($config)
+ {
+ $application=$this->getApplication();
+ if($this->_basePath===null)
+ $this->_basePath=dirname($application->getRequest()->getApplicationFilePath()).DIRECTORY_SEPARATOR.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=rtrim(dirname($application->getRequest()->getApplicationUrl()),'/\\').'/'.self::DEFAULT_BASEPATH;
+ $application->setAssetManager($this);
+ $this->_initialized=true;
+ }
+ public function getBasePath()
+ {
+ return $this->_basePath;
+ }
+ public function setBasePath($value)
+ {
+ if($this->_initialized)
+ throw new TInvalidOperationException('assetmanager_basepath_unchangeable');
+ else
+ {
+ $this->_basePath=Prado::getPathOfNamespace($value);
+ if($this->_basePath===null || !is_dir($this->_basePath) || !is_writable($this->_basePath))
+ throw new TInvalidDataValueException('assetmanager_basepath_invalid',$value);
+ }
+ }
+ public function getBaseUrl()
+ {
+ return $this->_baseUrl;
+ }
+ public function setBaseUrl($value)
+ {
+ if($this->_initialized)
+ throw new TInvalidOperationException('assetmanager_baseurl_unchangeable');
+ else
+ $this->_baseUrl=rtrim($value,'/');
+ }
+ public function publishFilePath($path,$checkTimestamp=false)
+ {
+ if(isset($this->_published[$path]))
+ return $this->_published[$path];
+ else if(empty($path) || ($fullpath=realpath($path))===false)
+ throw new TInvalidDataValueException('assetmanager_filepath_invalid',$path);
+ else if(is_file($fullpath))
+ {
+ $dir=$this->hash(dirname($fullpath));
+ $fileName=basename($fullpath);
+ $dst=$this->_basePath.DIRECTORY_SEPARATOR.$dir;
+ if(!is_file($dst.DIRECTORY_SEPARATOR.$fileName) || $checkTimestamp || $this->getApplication()->getMode()!==TApplicationMode::Performance)
+ $this->copyFile($fullpath,$dst);
+ return $this->_published[$path]=$this->_baseUrl.'/'.$dir.'/'.$fileName;
+ }
+ else
+ {
+ $dir=$this->hash($fullpath);
+ if(!is_dir($this->_basePath.DIRECTORY_SEPARATOR.$dir) || $checkTimestamp || $this->getApplication()->getMode()!==TApplicationMode::Performance)
+ {
+ $this->copyDirectory($fullpath,$this->_basePath.DIRECTORY_SEPARATOR.$dir);
+ }
+ return $this->_published[$path]=$this->_baseUrl.'/'.$dir;
+ }
+ }
+ public function getPublishedPath($path)
+ {
+ $path=realpath($path);
+ if(is_file($path))
+ return $this->_basePath.DIRECTORY_SEPARATOR.$this->hash(dirname($path)).DIRECTORY_SEPARATOR.basename($path);
+ else
+ return $this->_basePath.DIRECTORY_SEPARATOR.$this->hash($path);
+ }
+ public function getPublishedUrl($path)
+ {
+ $path=realpath($path);
+ if(is_file($path))
+ return $this->_baseUrl.'/'.$this->hash(dirname($path)).'/'.basename($path);
+ else
+ return $this->_baseUrl.'/'.$this->hash($path);
+ }
+ protected function hash($dir)
+ {
+ return sprintf('%x',crc32($dir.Prado::getVersion()));
+ }
+ protected function copyFile($src,$dst)
+ {
+ if(!is_dir($dst))
+ {
+ @mkdir($dst);
+ @chmod($dst, PRADO_CHMOD);
+ }
+ $dstFile=$dst.DIRECTORY_SEPARATOR.basename($src);
+ if(@filemtime($dstFile)<@filemtime($src))
+ {
+ @copy($src,$dstFile);
+ }
+ }
+ public function copyDirectory($src,$dst)
+ {
+ if(!is_dir($dst))
+ {
+ @mkdir($dst);
+ @chmod($dst, PRADO_CHMOD);
+ }
+ if($folder=@opendir($src))
+ {
+ while($file=@readdir($folder))
+ {
+ if($file==='.' || $file==='..' || $file==='.svn')
+ continue;
+ else if(is_file($src.DIRECTORY_SEPARATOR.$file))
+ {
+ if(@filemtime($dst.DIRECTORY_SEPARATOR.$file)<@filemtime($src.DIRECTORY_SEPARATOR.$file))
+ {
+ @copy($src.DIRECTORY_SEPARATOR.$file,$dst.DIRECTORY_SEPARATOR.$file);
+ @chmod($dst.DIRECTORY_SEPARATOR.$file, PRADO_CHMOD);
+ }
+ }
+ else
+ $this->copyDirectory($src.DIRECTORY_SEPARATOR.$file,$dst.DIRECTORY_SEPARATOR.$file);
+ }
+ closedir($folder);
+ } else {
+ throw new TInvalidDataValueException('assetmanager_source_directory_invalid', $src);
+ }
+ }
+ public function publishTarFile($tarfile, $md5sum, $checkTimestamp=false)
+ {
+ if(isset($this->_published[$md5sum]))
+ return $this->_published[$md5sum];
+ else if(($fullpath=realpath($md5sum))===false || !is_file($fullpath))
+ throw new TInvalidDataValueException('assetmanager_tarchecksum_invalid',$md5sum);
+ else
+ {
+ $dir=$this->hash(dirname($fullpath));
+ $fileName=basename($fullpath);
+ $dst=$this->_basePath.DIRECTORY_SEPARATOR.$dir;
+ if(!is_file($dst.DIRECTORY_SEPARATOR.$fileName) || $checkTimestamp || $this->getApplication()->getMode()!==TApplicationMode::Performance)
+ {
+ if(@filemtime($dst.DIRECTORY_SEPARATOR.$fileName)<@filemtime($fullpath))
+ {
+ $this->copyFile($fullpath,$dst);
+ $this->deployTarFile($tarfile,$dst);
+ }
+ }
+ return $this->_published[$md5sum]=$this->_baseUrl.'/'.$dir;
+ }
+ }
+ protected function deployTarFile($path,$destination)
+ {
+ if(($fullpath=realpath($path))===false || !is_file($fullpath))
+ throw new TIOException('assetmanager_tarfile_invalid',$path);
+ else
+ {
+ Prado::using('System.IO.TTarFileExtractor');
+ $tar = new TTarFileExtractor($fullpath);
+ return $tar->extract($destination);
+ }
+ }
+}
+class TGlobalization extends TModule
+{
+ private $_defaultCharset = 'UTF-8';
+ private $_defaultCulture = 'en';
+ private $_charset=null;
+ private $_culture=null;
+ private $_translation;
+ public function init($xml)
+ {
+ if($this->_charset===null)
+ $this->_charset=$this->getDefaultCharset();
+ if($this->_culture===null)
+ $this->_culture=$this->getDefaultCulture();
+ if($xml!==null)
+ {
+ $translation = $xml->getElementByTagName('translation');
+ if($translation)
+ $this->setTranslationConfiguration($translation->getAttributes());
+ }
+ $this->getApplication()->setGlobalization($this);
+ }
+ public function getDefaultCulture()
+ {
+ return $this->_defaultCulture;
+ }
+ public function setDefaultCulture($culture)
+ {
+ $this->_defaultCulture = str_replace('-','_',$culture);
+ }
+ public function getDefaultCharset()
+ {
+ return $this->_defaultCharset;
+ }
+ public function setDefaultCharset($charset)
+ {
+ $this->_defaultCharset = $charset;
+ }
+ public function getCulture()
+ {
+ return $this->_culture;
+ }
+ public function setCulture($culture)
+ {
+ $this->_culture = str_replace('-','_',$culture);
+ }
+ public function getCharset()
+ {
+ return $this->_charset;
+ }
+ public function setCharset($charset)
+ {
+ $this->_charset = $charset;
+ }
+ public function getTranslationConfiguration()
+ {
+ return $this->_translation;
+ }
+ protected function setTranslationConfiguration(TMap $config)
+ {
+ if($config['type'] == 'XLIFF' || $config['type'] == 'gettext')
+ {
+ if($config['source'])
+ {
+ $config['source'] = Prado::getPathOfNamespace($config['source']);
+ if(!is_dir($config['source']))
+ {
+ if(@mkdir($config['source'])===false)
+ throw new TConfigurationException('globalization_source_path_failed',
+ $config['source']);
+ chmod($config['source'], PRADO_CHMOD);
+ }
+ }
+ else
+ {
+ throw new TConfigurationException("invalid source dir '{$config['source']}'");
+ }
+ }
+ if($config['cache'])
+ {
+ $config['cache'] = $this->getApplication()->getRunTimePath().'/i18n';
+ if(!is_dir($config['cache']))
+ {
+ if(@mkdir($config['cache'])===false)
+ throw new TConfigurationException('globalization_cache_path_failed',
+ $config['cache']);
+ chmod($config['cache'], PRADO_CHMOD);
+ }
+ }
+ $this->_translation = $config;
+ }
+ public function getTranslationCatalogue()
+ {
+ return $this->_translation['catalogue'];
+ }
+ public function setTranslationCatalogue($value)
+ {
+ $this->_translation['catalogue'] = $value;
+ }
+ public function getCultureVariants($culture=null)
+ {
+ if($culture===null) $culture = $this->getCulture();
+ $variants = explode('_', $culture);
+ $result = array();
+ for(; count($variants) > 0; array_pop($variants))
+ $result[] = implode('_', $variants);
+ return $result;
+ }
+ public function getLocalizedResource($file,$culture=null)
+ {
+ $files = array();
+ $variants = $this->getCultureVariants($culture);
+ $path = pathinfo($file);
+ foreach($variants as $variant)
+ $files[] = $path['dirname'].DIRECTORY_SEPARATOR.$variant.DIRECTORY_SEPARATOR.$path['basename'];
+ $filename = substr($path['basename'],0,strrpos($path['basename'],'.'));
+ foreach($variants as $variant)
+ $files[] = $path['dirname'].DIRECTORY_SEPARATOR.$filename.'.'.$variant.'.'.$path['extension'];
+ $files[] = $file;
+ return $files;
+ }
+}
+class TApplication extends TComponent
+{
+ const STATE_OFF='Off';
+ const STATE_DEBUG='Debug';
+ const STATE_NORMAL='Normal';
+ const STATE_PERFORMANCE='Performance';
+ const PAGE_SERVICE_ID='page';
+ const CONFIG_FILE='application.xml';
+ const CONFIG_FILE_EXT='.xml';
+ const RUNTIME_PATH='runtime';
+ const CONFIGCACHE_FILE='config.cache';
+ const GLOBAL_FILE='global.cache';
+ private static $_steps=array(
+ 'onBeginRequest',
+ 'onLoadState',
+ 'onLoadStateComplete',
+ 'onAuthentication',
+ 'onAuthenticationComplete',
+ 'onAuthorization',
+ 'onAuthorizationComplete',
+ 'onPreRunService',
+ 'runService',
+ 'onSaveState',
+ 'onSaveStateComplete',
+ 'onPreFlushOutput',
+ 'flushOutput'
+ );
+ private $_id;
+ private $_uniqueID;
+ private $_requestCompleted=false;
+ private $_step;
+ private $_services;
+ private $_service;
+ private $_modules=array();
+ private $_parameters;
+ private $_configFile;
+ private $_basePath;
+ private $_runtimePath;
+ private $_stateChanged=false;
+ private $_globals=array();
+ private $_cacheFile;
+ private $_errorHandler;
+ private $_request;
+ private $_response;
+ private $_session;
+ private $_cache;
+ private $_statePersister;
+ private $_user;
+ private $_globalization;
+ private $_security;
+ private $_assetManager;
+ private $_authRules;
+ private $_mode=TApplicationMode::Debug;
+ private $_pageServiceID = self::PAGE_SERVICE_ID;
+ public function __construct($basePath='protected',$cacheConfig=true)
+ {
+ Prado::setApplication($this);
+ $this->resolvePaths($basePath);
+ if($cacheConfig)
+ $this->_cacheFile=$this->_runtimePath.DIRECTORY_SEPARATOR.self::CONFIGCACHE_FILE;
+ $this->_uniqueID=md5($this->_runtimePath);
+ $this->_parameters=new TMap;
+ $this->_services=array($this->getPageServiceID()=>array('TPageService',array(),null));
+ Prado::setPathOfAlias('Application',$this->_basePath);
+ }
+ protected function resolvePaths($basePath)
+ {
+ if(empty($basePath) || ($basePath=realpath($basePath))===false)
+ throw new TConfigurationException('application_basepath_invalid',$basePath);
+ if(is_file($basePath.DIRECTORY_SEPARATOR.self::CONFIG_FILE))
+ $configFile=$basePath.DIRECTORY_SEPARATOR.self::CONFIG_FILE;
+ else if(is_file($basePath))
+ {
+ $configFile=$basePath;
+ $basePath=dirname($configFile);
+ }
+ else
+ $configFile=null;
+ $runtimePath=$basePath.DIRECTORY_SEPARATOR.self::RUNTIME_PATH;
+ if(is_writable($runtimePath))
+ {
+ if($configFile!==null)
+ {
+ $runtimePath.=DIRECTORY_SEPARATOR.basename($configFile).'-'.Prado::getVersion();
+ if(!is_dir($runtimePath))
+ {
+ if(@mkdir($runtimePath)===false)
+ throw new TConfigurationException('application_runtimepath_failed',$runtimePath);
+ @chmod($runtimePath, PRADO_CHMOD); }
+ $this->setConfigurationFile($configFile);
+ }
+ $this->setBasePath($basePath);
+ $this->setRuntimePath($runtimePath);
+ }
+ else
+ throw new TConfigurationException('application_runtimepath_invalid',$runtimePath);
+ }
+ public function run()
+ {
+ try
+ {
+ $this->initApplication();
+ $n=count(self::$_steps);
+ $this->_step=0;
+ $this->_requestCompleted=false;
+ while($this->_step<$n)
+ {
+ if($this->_mode===self::STATE_OFF)
+ throw new THttpException(503,'application_unavailable');
+ if($this->_requestCompleted)
+ break;
+ $method=self::$_steps[$this->_step];
+ $this->$method();
+ $this->_step++;
+ }
+ }
+ catch(Exception $e)
+ {
+ $this->onError($e);
+ }
+ $this->onEndRequest();
+ }
+ public function completeRequest()
+ {
+ $this->_requestCompleted=true;
+ }
+ public function getRequestCompleted()
+ {
+ return $this->_requestCompleted;
+ }
+ public function getGlobalState($key,$defaultValue=null)
+ {
+ return isset($this->_globals[$key])?$this->_globals[$key]:$defaultValue;
+ }
+ public function setGlobalState($key,$value,$defaultValue=null)
+ {
+ $this->_stateChanged=true;
+ if($value===$defaultValue)
+ unset($this->_globals[$key]);
+ else
+ $this->_globals[$key]=$value;
+ }
+ public function clearGlobalState($key)
+ {
+ $this->_stateChanged=true;
+ unset($this->_globals[$key]);
+ }
+ protected function loadGlobals()
+ {
+ $this->_globals=$this->getApplicationStatePersister()->load();
+ }
+ protected function saveGlobals()
+ {
+ if($this->_stateChanged)
+ {
+ $this->_stateChanged=false;
+ $this->getApplicationStatePersister()->save($this->_globals);
+ }
+ }
+ public function getID()
+ {
+ return $this->_id;
+ }
+ public function setID($value)
+ {
+ $this->_id=$value;
+ }
+ public function getPageServiceID()
+ {
+ return $this->_pageServiceID;
+ }
+ public function setPageServiceID($value)
+ {
+ $this->_pageServiceID=$value;
+ }
+ public function getUniqueID()
+ {
+ return $this->_uniqueID;
+ }
+ public function getMode()
+ {
+ return $this->_mode;
+ }
+ public function setMode($value)
+ {
+ $this->_mode=TPropertyValue::ensureEnum($value,'TApplicationMode');
+ }
+ public function getBasePath()
+ {
+ return $this->_basePath;
+ }
+ public function setBasePath($value)
+ {
+ $this->_basePath=$value;
+ }
+ public function getConfigurationFile()
+ {
+ return $this->_configFile;
+ }
+ public function setConfigurationFile($value)
+ {
+ $this->_configFile=$value;
+ }
+ public function getRuntimePath()
+ {
+ return $this->_runtimePath;
+ }
+ public function setRuntimePath($value)
+ {
+ $this->_runtimePath=$value;
+ if($this->_cacheFile)
+ $this->_cacheFile=$this->_runtimePath.DIRECTORY_SEPARATOR.self::CONFIGCACHE_FILE;
+ $this->_uniqueID=md5($this->_runtimePath);
+ }
+ public function getService()
+ {
+ return $this->_service;
+ }
+ public function setService($value)
+ {
+ $this->_service=$value;
+ }
+ public function setModule($id,IModule $module)
+ {
+ if(isset($this->_modules[$id]))
+ throw new TConfigurationException('application_moduleid_duplicated',$id);
+ else
+ $this->_modules[$id]=$module;
+ }
+ public function getModule($id)
+ {
+ return isset($this->_modules[$id])?$this->_modules[$id]:null;
+ }
+ public function getModules()
+ {
+ return $this->_modules;
+ }
+ public function getParameters()
+ {
+ return $this->_parameters;
+ }
+ public function getRequest()
+ {
+ if(!$this->_request)
+ {
+ $this->_request=new THttpRequest;
+ $this->_request->init(null);
+ }
+ return $this->_request;
+ }
+ public function setRequest(THttpRequest $request)
+ {
+ $this->_request=$request;
+ }
+ public function getResponse()
+ {
+ if(!$this->_response)
+ {
+ $this->_response=new THttpResponse;
+ $this->_response->init(null);
+ }
+ return $this->_response;
+ }
+ public function setResponse(THttpResponse $response)
+ {
+ $this->_response=$response;
+ }
+ public function getSession()
+ {
+ if(!$this->_session)
+ {
+ $this->_session=new THttpSession;
+ $this->_session->init(null);
+ }
+ return $this->_session;
+ }
+ public function setSession(THttpSession $session)
+ {
+ $this->_session=$session;
+ }
+ public function getErrorHandler()
+ {
+ if(!$this->_errorHandler)
+ {
+ $this->_errorHandler=new TErrorHandler;
+ $this->_errorHandler->init(null);
+ }
+ return $this->_errorHandler;
+ }
+ public function setErrorHandler(TErrorHandler $handler)
+ {
+ $this->_errorHandler=$handler;
+ }
+ public function getSecurityManager()
+ {
+ if(!$this->_security)
+ {
+ $this->_security=new TSecurityManager;
+ $this->_security->init(null);
+ }
+ return $this->_security;
+ }
+ public function setSecurityManager(TSecurityManager $sm)
+ {
+ $this->_security=$sm;
+ }
+ public function getAssetManager()
+ {
+ if(!$this->_assetManager)
+ {
+ $this->_assetManager=new TAssetManager;
+ $this->_assetManager->init(null);
+ }
+ return $this->_assetManager;
+ }
+ public function setAssetManager(TAssetManager $value)
+ {
+ $this->_assetManager=$value;
+ }
+ public function getApplicationStatePersister()
+ {
+ if(!$this->_statePersister)
+ {
+ $this->_statePersister=new TApplicationStatePersister;
+ $this->_statePersister->init(null);
+ }
+ return $this->_statePersister;
+ }
+ public function setApplicationStatePersister(IStatePersister $persister)
+ {
+ $this->_statePersister=$persister;
+ }
+ public function getCache()
+ {
+ return $this->_cache;
+ }
+ public function setCache(ICache $cache)
+ {
+ $this->_cache=$cache;
+ }
+ public function getUser()
+ {
+ return $this->_user;
+ }
+ public function setUser(IUser $user)
+ {
+ $this->_user=$user;
+ }
+ public function getGlobalization($createIfNotExists=true)
+ {
+ if($this->_globalization===null && $createIfNotExists)
+ $this->_globalization=new TGlobalization;
+ return $this->_globalization;
+ }
+ public function setGlobalization(TGlobalization $glob)
+ {
+ $this->_globalization=$glob;
+ }
+ public function getAuthorizationRules()
+ {
+ if($this->_authRules===null)
+ $this->_authRules=new TAuthorizationRuleCollection;
+ return $this->_authRules;
+ }
+ public function applyConfiguration($config,$withinService=false)
+ {
+ if($config->getIsEmpty())
+ return;
+ foreach($config->getAliases() as $alias=>$path)
+ Prado::setPathOfAlias($alias,$path);
+ foreach($config->getUsings() as $using)
+ Prado::using($using);
+ if(!$withinService)
+ {
+ foreach($config->getProperties() as $name=>$value)
+ $this->setSubProperty($name,$value);
+ }
+ if(empty($this->_services))
+ $this->_services=array($this->getPageServiceID()=>array('TPageService',array(),null));
+ foreach($config->getParameters() as $id=>$parameter)
+ {
+ if(is_array($parameter))
+ {
+ $component=Prado::createComponent($parameter[0]);
+ foreach($parameter[1] as $name=>$value)
+ $component->setSubProperty($name,$value);
+ $this->_parameters->add($id,$component);
+ }
+ else
+ $this->_parameters->add($id,$parameter);
+ }
+ $modules=array();
+ foreach($config->getModules() as $id=>$moduleConfig)
+ {
+ list($moduleClass, $initProperties, $configElement)=$moduleConfig;
+ $module=Prado::createComponent($moduleClass);
+ if(!is_string($id))
+ {
+ $id='_module'.count($this->_modules);
+ $initProperties['id']=$id;
+ }
+ $this->setModule($id,$module);
+ foreach($initProperties as $name=>$value)
+ $module->setSubProperty($name,$value);
+ $modules[]=array($module,$configElement);
+ }
+ foreach($modules as $module)
+ $module[0]->init($module[1]);
+ foreach($config->getServices() as $serviceID=>$serviceConfig)
+ $this->_services[$serviceID]=$serviceConfig;
+ foreach($config->getExternalConfigurations() as $filePath=>$condition)
+ {
+ if($condition!==true)
+ $condition=$this->evaluateExpression($condition);
+ if($condition)
+ {
+ if(($path=Prado::getPathOfNamespace($filePath,self::CONFIG_FILE_EXT))===null || !is_file($path))
+ throw new TConfigurationException('application_includefile_invalid',$filePath);
+ $c=new TApplicationConfiguration;
+ $c->loadFromFile($path);
+ $this->applyConfiguration($c,$withinService);
+ }
+ }
+ }
+ protected function initApplication()
+ {
+ if($this->_configFile!==null)
+ {
+ if($this->_cacheFile===null || @filemtime($this->_cacheFile)<filemtime($this->_configFile))
+ {
+ $config=new TApplicationConfiguration;
+ $config->loadFromFile($this->_configFile);
+ if($this->_cacheFile!==null)
+ file_put_contents($this->_cacheFile,Prado::serialize($config),LOCK_EX);
+ }
+ else
+ $config=Prado::unserialize(file_get_contents($this->_cacheFile));
+ $this->applyConfiguration($config,false);
+ }
+ if(($serviceID=$this->getRequest()->resolveRequest(array_keys($this->_services)))===null)
+ $serviceID=$this->getPageServiceID();
+ $this->startService($serviceID);
+ }
+ public function startService($serviceID)
+ {
+ if(isset($this->_services[$serviceID]))
+ {
+ list($serviceClass,$initProperties,$configElement)=$this->_services[$serviceID];
+ $service=Prado::createComponent($serviceClass);
+ if(!($service instanceof IService))
+ throw new THttpException(500,'application_service_invalid',$serviceClass);
+ if(!$service->getEnabled())
+ throw new THttpException(500,'application_service_unavailable',$serviceClass);
+ $service->setID($serviceID);
+ $this->setService($service);
+ foreach($initProperties as $name=>$value)
+ $service->setSubProperty($name,$value);
+ if($configElement!==null)
+ {
+ $config=new TApplicationConfiguration;
+ $config->loadFromXml($configElement,$this->getBasePath());
+ $this->applyConfiguration($config,true);
+ }
+ $service->init($configElement);
+ }
+ else
+ throw new THttpException(500,'application_service_unknown',$serviceID);
+ }
+ public function onError($param)
+ {
+ Prado::log($param->getMessage(),TLogger::ERROR,'System.TApplication');
+ $this->raiseEvent('OnError',$this,$param);
+ $this->getErrorHandler()->handleError($this,$param);
+ }
+ public function onBeginRequest()
+ {
+ $this->raiseEvent('OnBeginRequest',$this,null);
+ }
+ public function onAuthentication()
+ {
+ $this->raiseEvent('OnAuthentication',$this,null);
+ }
+ public function onAuthenticationComplete()
+ {
+ $this->raiseEvent('OnAuthenticationComplete',$this,null);
+ }
+ public function onAuthorization()
+ {
+ $this->raiseEvent('OnAuthorization',$this,null);
+ }
+ public function onAuthorizationComplete()
+ {
+ $this->raiseEvent('OnAuthorizationComplete',$this,null);
+ }
+ public function onLoadState()
+ {
+ $this->loadGlobals();
+ $this->raiseEvent('OnLoadState',$this,null);
+ }
+ public function onLoadStateComplete()
+ {
+ $this->raiseEvent('OnLoadStateComplete',$this,null);
+ }
+ public function onPreRunService()
+ {
+ $this->raiseEvent('OnPreRunService',$this,null);
+ }
+ public function runService()
+ {
+ if($this->_service)
+ $this->_service->run();
+ }
+ public function onSaveState()
+ {
+ $this->raiseEvent('OnSaveState',$this,null);
+ $this->saveGlobals();
+ }
+ public function onSaveStateComplete()
+ {
+ $this->raiseEvent('OnSaveStateComplete',$this,null);
+ }
+ public function onPreFlushOutput()
+ {
+ $this->raiseEvent('OnPreFlushOutput',$this,null);
+ }
+ public function flushOutput()
+ {
+ $this->getResponse()->flush();
+ }
+ public function onEndRequest()
+ {
+ $this->saveGlobals(); $this->raiseEvent('OnEndRequest',$this,null);
+ }
+}
+class TApplicationMode extends TEnumerable
+{
+ const Off='Off';
+ const Debug='Debug';
+ const Normal='Normal';
+ const Performance='Performance';
+}
+class TApplicationConfiguration extends TComponent
+{
+ private $_properties=array();
+ private $_usings=array();
+ private $_aliases=array();
+ private $_modules=array();
+ private $_services=array();
+ private $_parameters=array();
+ private $_includes=array();
+ private $_empty=true;
+ public function loadFromFile($fname)
+ {
+ $dom=new TXmlDocument;
+ $dom->loadFromFile($fname);
+ $this->loadFromXml($dom,dirname($fname));
+ }
+ public function getIsEmpty()
+ {
+ return $this->_empty;
+ }
+ public function loadFromXml($dom,$configPath)
+ {
+ foreach($dom->getAttributes() as $name=>$value)
+ {
+ $this->_properties[$name]=$value;
+ $this->_empty=false;
+ }
+ foreach($dom->getElements() as $element)
+ {
+ switch($element->getTagName())
+ {
+ case 'paths':
+ $this->loadPathsXml($element,$configPath);
+ break;
+ case 'modules':
+ $this->loadModulesXml($element,$configPath);
+ break;
+ case 'services':
+ $this->loadServicesXml($element,$configPath);
+ break;
+ case 'parameters':
+ $this->loadParametersXml($element,$configPath);
+ break;
+ case 'include':
+ $this->loadExternalXml($element,$configPath);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ protected function loadPathsXml($pathsNode,$configPath)
+ {
+ foreach($pathsNode->getElements() as $element)
+ {
+ switch($element->getTagName())
+ {
+ case 'alias':
+ {
+ if(($id=$element->getAttribute('id'))!==null && ($path=$element->getAttribute('path'))!==null)
+ {
+ $path=str_replace('\\','/',$path);
+ if(preg_match('/^\\/|.:\\/|.:\\\\/',$path)) $p=realpath($path);
+ else
+ $p=realpath($configPath.DIRECTORY_SEPARATOR.$path);
+ if($p===false || !is_dir($p))
+ throw new TConfigurationException('appconfig_aliaspath_invalid',$id,$path);
+ if(isset($this->_aliases[$id]))
+ throw new TConfigurationException('appconfig_alias_redefined',$id);
+ $this->_aliases[$id]=$p;
+ }
+ else
+ throw new TConfigurationException('appconfig_alias_invalid');
+ $this->_empty=false;
+ break;
+ }
+ case 'using':
+ {
+ if(($namespace=$element->getAttribute('namespace'))!==null)
+ $this->_usings[]=$namespace;
+ else
+ throw new TConfigurationException('appconfig_using_invalid');
+ $this->_empty=false;
+ break;
+ }
+ default:
+ throw new TConfigurationException('appconfig_paths_invalid',$tagName);
+ }
+ }
+ }
+ protected function loadModulesXml($modulesNode,$configPath)
+ {
+ foreach($modulesNode->getElements() as $element)
+ {
+ if($element->getTagName()==='module')
+ {
+ $properties=$element->getAttributes();
+ $id=$properties->itemAt('id');
+ $type=$properties->remove('class');
+ if($type===null)
+ throw new TConfigurationException('appconfig_moduletype_required',$id);
+ $element->setParent(null);
+ if($id===null)
+ $this->_modules[]=array($type,$properties->toArray(),$element);
+ else
+ $this->_modules[$id]=array($type,$properties->toArray(),$element);
+ $this->_empty=false;
+ }
+ else
+ throw new TConfigurationException('appconfig_modules_invalid',$element->getTagName());
+ }
+ }
+ protected function loadServicesXml($servicesNode,$configPath)
+ {
+ foreach($servicesNode->getElements() as $element)
+ {
+ if($element->getTagName()==='service')
+ {
+ $properties=$element->getAttributes();
+ if(($id=$properties->itemAt('id'))===null)
+ throw new TConfigurationException('appconfig_serviceid_required');
+ if(($type=$properties->remove('class'))===null)
+ throw new TConfigurationException('appconfig_servicetype_required',$id);
+ $element->setParent(null);
+ $this->_services[$id]=array($type,$properties->toArray(),$element);
+ $this->_empty=false;
+ }
+ else
+ throw new TConfigurationException('appconfig_services_invalid',$element->getTagName());
+ }
+ }
+ protected function loadParametersXml($parametersNode,$configPath)
+ {
+ foreach($parametersNode->getElements() as $element)
+ {
+ if($element->getTagName()==='parameter')
+ {
+ $properties=$element->getAttributes();
+ if(($id=$properties->remove('id'))===null)
+ throw new TConfigurationException('appconfig_parameterid_required');
+ if(($type=$properties->remove('class'))===null)
+ {
+ if(($value=$properties->remove('value'))===null)
+ $this->_parameters[$id]=$element;
+ else
+ $this->_parameters[$id]=$value;
+ }
+ else
+ $this->_parameters[$id]=array($type,$properties->toArray());
+ $this->_empty=false;
+ }
+ else
+ throw new TConfigurationException('appconfig_parameters_invalid',$element->getTagName());
+ }
+ }
+ protected function loadExternalXml($includeNode,$configPath)
+ {
+ if(($when=$includeNode->getAttribute('when'))===null)
+ $when=true;
+ if(($filePath=$includeNode->getAttribute('file'))===null)
+ throw new TConfigurationException('appconfig_includefile_required');
+ if(isset($this->_includes[$filePath]))
+ $this->_includes[$filePath]='('.$this->_includes[$filePath].') || ('.$when.')';
+ else
+ $this->_includes[$filePath]=$when;
+ $this->_empty=false;
+ }
+ public function getProperties()
+ {
+ return $this->_properties;
+ }
+ public function getAliases()
+ {
+ return $this->_aliases;
+ }
+ public function getUsings()
+ {
+ return $this->_usings;
+ }
+ public function getModules()
+ {
+ return $this->_modules;
+ }
+ public function getServices()
+ {
+ return $this->_services;
+ }
+ public function getParameters()
+ {
+ return $this->_parameters;
+ }
+ public function getExternalConfigurations()
+ {
+ return $this->_includes;
+ }
+}
+class TApplicationStatePersister extends TModule implements IStatePersister
+{
+ const CACHE_NAME='prado:appstate';
+ public function init($config)
+ {
+ $this->getApplication()->setApplicationStatePersister($this);
+ }
+ protected function getStateFilePath()
+ {
+ return $this->getApplication()->getRuntimePath().'/global.cache';
+ }
+ public function load()
+ {
+ if(($cache=$this->getApplication()->getCache())!==null && ($value=$cache->get(self::CACHE_NAME))!==false)
+ return unserialize($value);
+ else
+ {
+ if(($content=@file_get_contents($this->getStateFilePath()))!==false)
+ return unserialize($content);
+ else
+ return null;
+ }
+ }
+ public function save($state)
+ {
+ $content=serialize($state);
+ $saveFile=true;
+ if(($cache=$this->getApplication()->getCache())!==null)
+ {
+ if($cache->get(self::CACHE_NAME)===$content)
+ $saveFile=false;
+ else
+ $cache->set(self::CACHE_NAME,$content);
+ }
+ if($saveFile)
+ {
+ $fileName=$this->getStateFilePath();
+ file_put_contents($fileName,$content,LOCK_EX);
+ }
+ }
+}
+class TShellApplication extends TApplication
+{
+ public function run()
+ {
+ $this->initApplication();
+ }
+}
+?> \ No newline at end of file
diff --git a/index.html b/index.html
index 0a6fbd86..233f4a38 100644
--- a/index.html
+++ b/index.html
@@ -92,6 +92,7 @@ PRADO component tags when you use it to edit PRADO templates.
<li><a href="mailto:haertl.mike@googlemail.com">Michael Härtl</a></li>
<li><a href="mailto:eirikhm@gmail.com">Eirik Hoem</a></li>
<li><a href="mailto:godzilla80@gmx.net">Yves Berkholz</a></li>
+<li><a href="mailto:kamikazeole@gmail.com">Ole Helgesen</a></li>
</ul>
<h3>Translations</h3>
diff --git a/tests/FunctionalTests/active-controls/protected/pages/ActiveDatePicker.page b/tests/FunctionalTests/active-controls/protected/pages/ActiveDatePicker.page
index 64f37867..88117744 100755
--- a/tests/FunctionalTests/active-controls/protected/pages/ActiveDatePicker.page
+++ b/tests/FunctionalTests/active-controls/protected/pages/ActiveDatePicker.page
@@ -1,10 +1,10 @@
<html>
<com:THead/>
<body>
-<com:TForm>
+<com:TForm>
<h1>TActiveDatePicker test</h1>
-<p>
-<com:TActiveDatePicker ID="datepicker" DateFormat="MM-dd-yyyy" OnCallback="testDatePicker"/>
+<p>
+<com:TActiveDatePicker ID="datepicker" DateFormat="MM-dd-yyyy" OnCallback="testDatePicker"/>
<com:TActiveLabel ID="status" /><br/>
<com:TActiveButton ID="decreaseButton" OnClick="decrease" Text="-1" OnCallback="testDatePicker"/>
<com:TActiveButton ID="todayButton" OnClick="today" Text="Today" OnCallback="testDatePicker"/>
@@ -18,8 +18,11 @@ ShowCalendar=false
<com:TActiveDatePicker ID="datepicker2" DateFormat="MM-dd-yyyy" OnCallback="testDatePicker2" InputMode="DropDownList" ShowCalendar="false"/>
<com:TActiveLabel ID="status2" /><br/>
</p>
+<p>Partial Calendar</p>
+<p>
+<com:TActiveDatePicker ID="datepicker3" DateFormat="MMMM/yyyy" OnCallback="testDatePicker3" InputMode="DropDownList" ShowCalendar="true"/>
+<com:TActiveLabel ID="status3" /><br/>
-
</com:TForm>
</body>
</html> \ No newline at end of file
diff --git a/tests/FunctionalTests/active-controls/protected/pages/ActiveDatePicker.php b/tests/FunctionalTests/active-controls/protected/pages/ActiveDatePicker.php
index 4897a146..300574aa 100755
--- a/tests/FunctionalTests/active-controls/protected/pages/ActiveDatePicker.php
+++ b/tests/FunctionalTests/active-controls/protected/pages/ActiveDatePicker.php
@@ -17,7 +17,11 @@ class ActiveDatePicker extends TPage {
public function testDatePicker2($sender, $param){
$this->status2->Text = $this->datepicker2->getText();
}
-
+
+ public function testDatePicker3($sender, $param){
+ $this->status3->Text = $this->datepicker3->getText();
+ }
+
public function today ($sender, $param)
{
$this->datepicker->setTimestamp(time());
diff --git a/tests/FunctionalTests/active-controls/tests/ActiveDatePickerTestCase.php b/tests/FunctionalTests/active-controls/tests/ActiveDatePickerTestCase.php
index 5faee611..c3b47934 100755
--- a/tests/FunctionalTests/active-controls/tests/ActiveDatePickerTestCase.php
+++ b/tests/FunctionalTests/active-controls/tests/ActiveDatePickerTestCase.php
@@ -104,6 +104,16 @@ class ActiveDatePickerTestCase extends SeleniumTestCase
$this->pause(800);
$dateToCheck=mktime(0,0,0,(int)date('m'),(int)date('d'), 2005);
$this->verifyText("status2", date('m-d-Y', $dateToCheck));
+
+
+ $this->verifyText("status3", "");
+ $dateToCheck=time();
+ $this->verifySelected("datepicker3_month", date('F', $dateToCheck));
+ $this->verifySelected("datepicker3_year", date('Y', $dateToCheck));
+ $this->select("datepicker3_year", 2005);
+ $this->pause(800);
+ $dateToCheck=mktime(0,0,0,(int)date('m'),(int)date('d'), 2005);
+ $this->verifyText("status3", date('m/Y', $dateToCheck));
}
}
diff --git a/tests/FunctionalTests/tickets/protected/pages/Issue216.page b/tests/FunctionalTests/tickets/protected/pages/Issue216.page
new file mode 100644
index 00000000..2efff728
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected/pages/Issue216.page
@@ -0,0 +1,15 @@
+<com:TContent ID="Content">
+ <h1>TTabPanel doesn't preserve active tab on callback request</h1>
+
+ <com:TTabPanel id="tabpanel">
+ <com:TTabView id="tab1" Caption="Tab 1">
+ <p>This is Tab 1</p>
+ </com:TTabView>
+ <com:TTabView id="tab2" Caption="Tab 2">
+ <p>This is Tab 2</p>
+ </com:TTabView>
+ </com:TTabPanel>
+
+ <com:TActiveButton id="btn1" OnCallback="buttonClickCallback" Text ="Click me"/>
+ <com:TActiveLabel id="result"/>
+</com:TContent> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected/pages/Issue216.php b/tests/FunctionalTests/tickets/protected/pages/Issue216.php
new file mode 100644
index 00000000..15491e2e
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected/pages/Issue216.php
@@ -0,0 +1,14 @@
+<?php
+Prado::using('System.Web.UI.ActiveControls.*');
+
+class Issue216 extends TPage
+{
+ public function buttonClickCallback($sender, $param)
+ {
+
+ $this->result->setText('Tab ActiveIndex is : '.$this->tabpanel->ActiveViewIndex);
+
+ }
+}
+
+?> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/tests/Issue216TestCase.php b/tests/FunctionalTests/tickets/tests/Issue216TestCase.php
new file mode 100644
index 00000000..1cfdf0cd
--- /dev/null
+++ b/tests/FunctionalTests/tickets/tests/Issue216TestCase.php
@@ -0,0 +1,28 @@
+<?php
+
+class Issue216TestCase extends SeleniumTestCase
+{
+ function test()
+ {
+ $this->open('tickets/index.php?page=Issue216');
+ $this->assertTextPresent('TTabPanel doesn\'t preserve active tab on callback request');
+
+ $this->assertVisible('ctl0_Content_tab1');
+
+ $this->click("ctl0_Content_btn1");
+ $this->pause(800);
+
+ $this->assertText("ctl0_Content_result", "Tab ActiveIndex is : 0");
+
+ $this->click("ctl0_Content_tab2_0");
+ $this->pause(800);
+
+ $this->assertVisible('ctl0_Content_tab2');
+
+ $this->click("ctl0_Content_btn1");
+ $this->pause(800);
+ $this->assertText("ctl0_Content_result", "Tab ActiveIndex is : 1");
+ }
+}
+
+?> \ No newline at end of file