From 94e94e0a8566f23d16658a04c55b0bbfdd6689aa Mon Sep 17 00:00:00 2001 From: "godzilla80@gmx.net" <> Date: Sun, 14 Feb 2010 01:22:57 +0000 Subject: Merge Branches & Trunk /trunk:r2680,2692,2707-2736 /branches/3.1:r2682-2686,2694-2702,2705,2738-2762 --- .gitattributes | 4 + HISTORY | 36 +- UPGRADE | 7 + buildscripts/texbuilder/quickstart/quickstart.tex | 2 +- .../protected/pages/Controls/JavascriptLogger.page | 2 +- .../pages/Controls/id/JavascriptLogger.page | 2 +- framework/3rdParty/WsdlGen/Wsdl.php | 19 +- framework/3rdParty/readme.html | 2 +- framework/Caching/TDbCache.php | 11 +- .../Relations/TActiveRecordRelation.php | 4 +- framework/Data/Common/Mysql/TMysqlMetaData.php | 14 +- framework/Data/Common/TDbCommandBuilder.php | 154 +- framework/Data/Common/TDbTableInfo.php | 24 +- framework/Data/DataGateway/TDataGatewayCommand.php | 5 +- framework/Data/DataGateway/TSqlCriteria.php | 72 +- .../SqlMap/Configuration/TSqlMapCacheModel.php | 2 +- .../Configuration/TSqlMapXmlConfiguration.php | 2 +- framework/Data/SqlMap/DataMapper/TSqlMapCache.php | 80 +- framework/Data/SqlMap/TSqlMapConfig.php | 43 +- framework/Data/TDbConnection.php | 53 +- framework/Exceptions/messages/messages-fr.txt | 1 + framework/Exceptions/messages/messages-id.txt | 1 + framework/Exceptions/messages/messages-zh.txt | 1 + framework/Exceptions/messages/messages.txt | 1 + framework/TShellApplication.php | 10 +- framework/Util/TLogRouter.php | 87 +- .../prado/activecontrols/activedatepicker.js | 15 +- .../prado/activefileupload/activefileupload.js | 41 +- .../source/prado/validator/validation3.js | 5 +- framework/Web/THttpRequest.php | 6 +- framework/Web/THttpResponse.php | 29 +- framework/Web/THttpSession.php | 12 +- framework/Web/UI/ActiveControls/TDropContainer.php | 7 + framework/Web/UI/WebControls/TBaseValidator.php | 3 +- framework/Web/UI/WebControls/TBoundColumn.php | 2 +- framework/Web/UI/WebControls/TCheckBoxList.php | 99 +- framework/Web/UI/WebControls/TDataGridColumn.php | 6 +- framework/Web/UI/WebControls/TJavascriptLogger.php | 6 +- .../UI/WebControls/TRegularExpressionValidator.php | 4 +- framework/Web/UI/WebControls/TTabPanel.php | 1 + framework/Web/UI/WebControls/TTextBox.php | 14 +- .../Web/UI/WebControls/TValidationSummary.php | 17 + framework/pradolite.php | 9489 ++++++++++++++++++++ index.html | 1 + .../protected/pages/ActiveDatePicker.page | 11 +- .../protected/pages/ActiveDatePicker.php | 6 +- .../tests/ActiveDatePickerTestCase.php | 10 + .../tickets/protected/pages/Issue216.page | 15 + .../tickets/protected/pages/Issue216.php | 14 + .../tickets/tests/Issue216TestCase.php | 28 + 50 files changed, 10294 insertions(+), 186 deletions(-) create mode 100644 framework/pradolite.php create mode 100644 tests/FunctionalTests/tickets/protected/pages/Issue216.page create mode 100644 tests/FunctionalTests/tickets/protected/pages/Issue216.php create mode 100644 tests/FunctionalTests/tickets/tests/Issue216TestCase.php diff --git a/.gitattributes b/.gitattributes index 868a31ba..964ffa6d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2896,6 +2896,7 @@ framework/powered2.gif -text framework/prado-cli -text framework/prado-cli.bat -text framework/prado.php -text +framework/pradolite.php -text /index.html -text /phing -text /phing.bat -text @@ -3127,6 +3128,8 @@ tests/FunctionalTests/tickets/protected/messages/en/messages.xml -text tests/FunctionalTests/tickets/protected/pages/DActiveDropDownList2.php -text tests/FunctionalTests/tickets/protected/pages/Issue120.page -text tests/FunctionalTests/tickets/protected/pages/Issue120.php -text +tests/FunctionalTests/tickets/protected/pages/Issue216.page -text +tests/FunctionalTests/tickets/protected/pages/Issue216.php -text tests/FunctionalTests/tickets/protected/pages/Layout.php -text tests/FunctionalTests/tickets/protected/pages/Layout.tpl -text tests/FunctionalTests/tickets/protected/pages/TestHtmlArea.php -text @@ -3301,6 +3304,7 @@ tests/FunctionalTests/tickets/protected700/pages/admin/users/config.xml -text tests/FunctionalTests/tickets/protected700/pages/config.xml -text tests/FunctionalTests/tickets/protected700/pages/content/Home.page -text tests/FunctionalTests/tickets/tests/Issue120TestCase.php -text +tests/FunctionalTests/tickets/tests/Issue216TestCase.php -text tests/FunctionalTests/tickets/tests/Ticket121TestCase.php -text tests/FunctionalTests/tickets/tests/Ticket163TestCase.php -text tests/FunctionalTests/tickets/tests/Ticket169TestCase.php -text diff --git a/HISTORY b/HISTORY index 68d4d983..47a884e0 100644 --- a/HISTORY +++ b/HISTORY @@ -13,13 +13,46 @@ ENH: Issue#173 - Add "dragdropextra" (superghosting) patch, mouse coordinates an NEW: Add TActiveMultiView (LCS Team) NEW: Beta of master/slave senario solution (Yves) -Version 3.1.6 to be released +Version 3.1.7 To be released +ENH: Issue#24 - Specify needed fields on demand (Yves) +BUG: Issue#80 - Inconsistencies in TRegularExpressionValidator (Christophe) +BUG: Issue#86 - THttpSession.CookieMode ignored / Session ID leak (Christophe) +BUG: Issue#94 - DataGrid header/footer renderers unable to locate their parent grid in setData() method (Christophe) +BUG: Issue#151 - TTextBox fails to display inital line break (Yves) +BUG: Issue#153 - Bug with calls like MyActiveRedorc->withText()->withUser()->find(...) and null result (Christophe) +BUG: Issue#157 - Enabled does not work properly on TActiveRadioButton/CheckBoxList controls (Bradley, Carl) +BUG: Issue#166 - E_NOTICE level error in TDataGatewayCommand (Carl) +BUG: Issue#169 - FlushOnExecute on Basic CacheModel flushes all Application Cache (E.Letard, Christophe) +BUG: Issue#171 - tag in SqlMap config ignored in 3.1.5 and above, introduced by solving Issue#68 (Yves) +EHN: Issue#184 - THttpResponse doesn't support custom Content-Type headers, remove charset part of header if THttpResponse.Charset=false (Yves) +BUG: Issue#188 - TDbCache doesn't check if db connection is active. (Yves) +BUG: Issue#189 - Page State corrupted when EnableStateValidation=False (Christophe) +BUG: Issue#191 - Bad parsing of MySQL ENUM type column (Yves) +BUG: Issue#192 - soap-enc:Array not a valid complex type (mosonyi at esix.hu) +BUG: Issue#198 - "Undefined variable: tagName" after error in application configuration. (Christophe) +BUG: Issue#200 - TShellApplication failed when no service are defined in application configuration. (Christophe) +BUG: Issue#208 - TDbConnection.Charset not working properly (googlenew at pcforum.hu, Christophe) +BUG: Issue#212 - Mistaken query executed by TMysqlMetaData (pbenny, Christophe) +BUG: Issue#216 - TTabPanel doesn't preserve active tab on callback request (googlenew at pcforum.hu,Christophe) +BUG: Typo in TBoundColumn (Robin) +BUG: TActiveDatePicker js error when date format does not have the 3 elements (Christophe) +ENH: Add property ClientScriptManagerClass to TPageService and releated changes in TPage.getClientScript() (Yves) +ENH: Always render clientside counterparts of validation control even if not enabled, but pass-through Enabled property, to allow Enabled/Disable of validator on callback. (Yves) +EHN: Add property TValidationSummary.ScrollToSummary to server-side control since property exists on client-side. (Yves) +EHN: Add property TransactionClass (defaults to System.Data.TDbTransaction) to TDbConnection and modify beginTransaction() (Yves) +ENH: Modify TDbTableInfo::getColumnNames() to store result in private class member (Yves) +ENH: Issue#215 - Add ClientSide property to TDropContainer (googlenew at pcforum.hu, Christophe) +CHG: Issue#218 - Change URL of Javascript Logger (Christophe) + +Version 3.1.6 July 22, 2009 BUG: Issue#98 - Missing file in quickstart demo (Chrisotphe) +BUG: Issue#105 - Enabling/disabling TFileUpload on TPanel (Carl) BUG: Issue#115 - Clientside registry of serverside controls don't cover all controls: Prado.WebUI.TAutoComplete, Prado.WebUI.TInPlaceTextBox (Yves) BUG: Issue#117 - Consider TValidationSummary.DisplayMode="HeaderOnly" if TValidationSummary.ShowMessageBox is set (Yves) BUG: Issue#164 - CultureInfo::validCulture should be declared as a static method (Christophe) BUG: Issue#168 - TSqlMapXmlConfiguration: CacheModel properties are not set (Yves) BUG: Issue#172 - TDbCache fails to recognize need to re-create cache table (Yves) +BUG: Issue#178 - TActiveFileUpload progress indicator is misleading (Christophe, googlenew-at-pcforum.hu) ENH: Issue#174 - TErrorHandler: HTTP error messages contains sensitive information (Yves) ENH: Issue#175 - TBulletedList: Introduce TBulletStyle::None (Yves) ENH: Issue#176 - THttpResponse::writeFile() add additional parameters to take more influence on behavior (forceDownload, clientFileName, fileSize), add png to defaultMimeTypes (Yves) @@ -28,6 +61,7 @@ ENH: TAssetManager: introduce protected property "Published" to allow subclasses ENH: TFirePhpLogRoute: bypass to TBrowserLogRoute if headers already sent / php.ini (output_buffering=Off, implicit_flush=On) (Yves) EHN: Performance optimization in PradoBase::createComponent() (Yves) EHN: Add property TPage.EnableStateCompression and related modifications in TPageStateFormatter since it is unnecessary to compress clientstate if TCachePageStatePersister/TSessionPageStatePersister is used (Yves) +ENH: Add property CssClass to TBrowserLog otuput and hide inline CSS (Carl) Version 3.1.5 May 24, 2009 BUG: Issue#55 - TPropertyAccess.get and has don't recognize magic getter __get (Yves) diff --git a/UPGRADE b/UPGRADE index c21495d9..6d4dae73 100644 --- a/UPGRADE +++ b/UPGRADE @@ -9,6 +9,13 @@ if you want to upgrade from version A to version C and there is version B between A and C, you need to following the instructions for both A and B. +Upgrading from v3.1.6 +--------------------- +- The different SQLMap cache engines (TSQLMapFifoCache, TSQLMapLRUCache, TSQLMapApplicationCache) doesn't +take anymore the cache size in their constructor. Instead, they take the cachemodel object who instanciated them. +It shouldn't affect existing code, except if you instanciate one of this cache directly (i.e, without a +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 @@

-TJavascriptLogger provides logging for client-side javascript. It is mainly a wrapper of the Javascript developed at http://gleepglop.com/javascripts/logger/. +TJavascriptLogger provides logging for client-side javascript. It is mainly a wrapper of the Javascript developed at http://gleepglop.com/javascripts/logger/.

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 @@

-TJavascriptLogger menyediakan javascript pencatatan sisi-klien. Ini sebagian besar adalah pelapis dari Javascript yang dikembangkan di http://gleepglop.com/javascripts/logger/. +TJavascriptLogger menyediakan javascript pencatatan sisi-klien. Ini sebagian besar adalah pelapis dari Javascript yang dikembangkan di http://gleepglop.com/javascripts/logger/.

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. ../Web/Javascripts/prado/logger/logger.js - http://gleepglop.com/javascripts/logger/ http://slayeroffice.com + http://web.archive.org/web/20060512041505/gleepglop.com/javascripts/logger/ http://slayeroffice.com None TJavascriptLogger Javascript logger by Corey Johnson. Object Tree by S.G. Chipman. 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 * @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 * @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 @@ -156,6 +156,147 @@ class TDbCommandBuilder extends TComponent return '('.implode(' AND ', $conditions).')'; } + /** + * + * 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 + * + * array('col1', 'col2', 'col2') + * // SELECT `col1`, `col2`, `col3` FROM... + * + * + * - Column aliasing + * + * array('mycol1' => 'col1', 'mycol2' => 'COUNT(*)') + * // SELECT `col1` AS mycol1, COUNT(*) AS mycol2 FROM... + * + * + * - NULL and scalar values (strings will be quoted depending on database) + * + * array('col1' => 'my custom string', 'col2' => 1.0, 'col3' => 'NULL') + * // SELECT "my custom string" AS `col1`, 1.0 AS `col2`, NULL AS `col3` FROM... + * + * + * - If the *-wildcard char is used as key or value, add the full list of quoted table column names + * + * array('col1' => 'NULL', '*') + * // SELECT `col1`, `col2`, `col3`, NULL AS `col1` FROM... + * + * @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. @@ -163,10 +304,11 @@ class TDbCommandBuilder extends TComponent * @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; @@ -55,6 +60,60 @@ class TSqlCriteria extends TComponent $this->setCondition($condition); } + /** + * 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 + * + * array('col1', 'col2', 'col2') + * // SELECT `col1`, `col2`, `col3` FROM... + * + * + * - Column aliasing + * + * array('mycol1' => 'col1', 'mycol2' => 'COUNT(*)') + * // SELECT `col1` AS mycol1, COUNT(*) AS mycol2 FROM... + * + * + * - NULL and scalar values (strings will be quoted depending on database) + * + * array('col1' => 'my custom string', 'col2' => 1.0, 'col3' => 'NULL') + * // SELECT "my custom string" AS `col1`, 1.0 AS `col2`, NULL AS `col3` FROM... + * + * + * - If the *-wildcard char is used as key or value, add the full list of quoted table column names + * + * array('col1' => 'NULL', '*') + * // SELECT `col1`, `col2`, `col3`, NULL AS `col1` FROM... + * + * + * @param mixed + * @since 3.1.7 + * @see TDbCommandBuilder::getSelectFieldList() + */ + public function setSelect($value) + { + $this->_select = $value; + } + /** * @return string search conditions. */ @@ -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 * @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 @@ -52,6 +52,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: * @@ -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=''; @@ -92,13 +98,19 @@ class TDbConnection extends TComponent private $_pdo=null; 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,12 +336,31 @@ 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) 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 * @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 = <<getCssClass()) + { + $string = << + + + Application Log + + +   + CategoryMessageTime Spent (s)Cumulated Time Spent (s) + +EOD; + } + else + { + $string = << @@ -673,18 +711,36 @@ class TBrowserLogRoute extends TLogRoute CategoryMessageTime Spent (s)Cumulated Time Spent (s) 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 = <<getCssClass()) + { + $colorCssClass = $log[1]; + $messageCssClass = $info['even'] ? 'even' : 'odd'; + $string = << +   + {$log[2]} + {$msg} + {$delta} + {$total} + +EOD; + } + else + { + $bgcolor = $info['even'] ? "#fff" : "#eee"; + $color = $this->getColorLevel($log[1]); + $string = <<   {$log[2]} @@ -693,6 +749,7 @@ EOD; {$total} EOD; + } return $string; } @@ -713,13 +770,25 @@ EOD; protected function renderFooter() { - $string = ""; - foreach(self::$_levelValues as $name => $level) + $string = ''; + if($this->getCssClass()) { - $string .= "getColorLevel($level); - $string .= ";margin: 0.5em; padding:0.01em;\">".strtoupper($name).""; + $string .= ''; + foreach(self::$_levelValues as $name => $level) + { + $string .= ''.strtoupper($name).""; + } + } + else + { + $string .= ""; + foreach(self::$_levelValues as $name => $level) + { + $string .= "getColorLevel($level); + $string .= ";margin: 0.5em; padding:0.01em;\">".strtoupper($name).""; + } } - $string .= ""; + $string .= ''; 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 * @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 * @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 * @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 * @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 * @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 * @version $Id$ @@ -84,7 +84,7 @@ class TJavascriptLogger extends TWebControl public function renderContents($writer) { $code = strtoupper($this->getToggleKey()); - $info = '(more info).'; + $info = '(more info).'; $link = 'toggle the javascript log console.'; $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 * @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. */ @@ -298,6 +298,18 @@ class TTextBox extends TWebControl implements IPostBackDataHandler, IValidatable $writer->write(THttpUtility::htmlEncode($this->getText())); } + /** + * 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 */ 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 @@ -148,6 +148,22 @@ class TValidationSummary extends TWebControl $this->setViewState('ShowSummary',TPropertyValue::ensureBoolean($value),true); } + /** + * @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. */ @@ -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 @@ +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 'Powered by PRADO'; + } + 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 '

Fatal Error

'; + echo '

'.$msg.'

'; + if(!function_exists('debug_backtrace')) + return; + echo '

Debug Backtrace

'; + echo '
';
+		$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 '';
+			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 '
'; + 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'].' PRADO/'.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 "Recursive Error\n"; + echo "

Recursive Error

\n"; + echo "
".$exception->__toString()."
\n"; + echo ""; + } + 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'].' PRADO/'.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.="
".$line."
"; + } + 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/',"\${1}",$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."_tagName}>"; + return $str; + } + else if(($value=$this->getValue())!=='') + { + $value=$this->xmlEncode($value); + return $prefix."<{$this->_tagName}$attr>$value_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 "\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 '\n"; + } + public static function renderScriptBlocks($scripts) + { + if(count($scripts)) + return "\n"; + else + return ''; + } + public static function renderScriptBlock($script) + { + return "\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_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_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_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.="\n"; + else + $str.="\n"; + } + $writer->write($str); + } + public function renderStyleSheets($writer) + { + if(count($this->_styleSheets)) + $writer->write("\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.='\n"; + } + else + { + $str.='\n"; + } + } + if($str!=='') + $writer->write("
\n".$str."
\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*%>|<%[%#~\/\\$=\\[](.*?)%>|)*)\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,'$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,'$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',""); + $name=array_pop($stack); + if($name!==$type) + { + $tag=$name[0]==='@' ? '' : ""; + 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,'')===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,'"); + $name=array_pop($stack); + if($name!=='@'.$prop) + { + $tag=$name[0]==='@' ? '' : ""; + 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,'