diff options
author | godzilla80@gmx.net <> | 2010-02-14 01:22:57 +0000 |
---|---|---|
committer | godzilla80@gmx.net <> | 2010-02-14 01:22:57 +0000 |
commit | 94e94e0a8566f23d16658a04c55b0bbfdd6689aa (patch) | |
tree | 72ffad82c279080dd9320d45dda26d64ffb4626f | |
parent | 966fd66f217911d079c4bd6a87b09f4a0c5c4736 (diff) |
Merge Branches & Trunk
/trunk:r2680,2692,2707-2736
/branches/3.1:r2682-2686,2694-2702,2705,2738-2762
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 @@ -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) @@ -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 © 2005-2008 PradoSoft + * @copyright Copyright © 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 © 2005-2008 PradoSoft + * @copyright Copyright © 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 © 2005-2008 PradoSoft + * @copyright Copyright © 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 © 2005-2008 PradoSoft + * @copyright Copyright © 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> </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"> </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;"> </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?'&':'&')) . 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 © 2005-2008 PradoSoft + * @copyright Copyright © 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 © 2005-2008 PradoSoft + * @copyright Copyright © 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 © 2005-2008 PradoSoft + * @copyright Copyright © 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 © 2005-2008 PradoSoft + * @copyright Copyright © 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 © 2005-2008 PradoSoft + * @copyright Copyright © 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 © 2005-2008 PradoSoft + * @copyright Copyright © 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( + '>'=>'>', + '<'=>'<', + '&'=>'&', + '"'=>'"', + "\r"=>'
', + "\t"=>'	', + "\n"=>'
')); + } +} +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('<'=>'<','>'=>'>','"'=>'"'); + private static $_decodeTable=array('<'=>'<','>'=>'>','"'=>'"'); + 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?'&':'&'; + $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?'&':'&')) . 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('&','&',$url), true, $this->_status); + else + header('Location: '.str_replace('&','&',$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('&','&',str_replace('&','&',$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.='&mode=debug'; + } + if($gzip===false) + $url.='&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 @@ -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 |