From 6f00b28a1d9c7409c956a83866eac48a9493e83c Mon Sep 17 00:00:00 2001 From: "ctrlaltca@gmail.com" <> Date: Sat, 21 May 2011 17:10:29 +0000 Subject: branch/3.1: merged bugfixesfrom trunk/ up to r2880; correct svn:mergeinfo property --- .gitattributes | 1 + HISTORY | 25 + framework/Collections/TListItemCollection.php | 164 ++++++ framework/Collections/TQueue.php | 8 +- framework/I18N/Translation.php | 201 ++++---- framework/PradoBase.php | 27 +- framework/Security/TSecurityManager.php | 547 ++++++++++----------- framework/Util/TSimpleDateFormatter.php | 3 +- .../source/prado/activecontrols/activecontrols3.js | 1 - framework/Web/TUrlMapping.php | 282 +++++------ .../UI/ActiveControls/TCallbackClientScript.php | 3 +- framework/Web/UI/TTemplateManager.php | 12 +- framework/Web/UI/WebControls/TBoundColumn.php | 16 +- framework/Web/UI/WebControls/TDatePicker.php | 4 +- framework/Web/UI/WebControls/TListControl.php | 151 +----- tests/unit/phpunit.php | 18 +- 16 files changed, 765 insertions(+), 698 deletions(-) create mode 100644 framework/Collections/TListItemCollection.php diff --git a/.gitattributes b/.gitattributes index 772a2b7d..f31bb494 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2086,6 +2086,7 @@ framework/Caching/TXCache.php -text framework/Collections/TAttributeCollection.php -text framework/Collections/TDummyDataSource.php -text framework/Collections/TList.php -text +framework/Collections/TListItemCollection.php -text framework/Collections/TMap.php -text framework/Collections/TPagedDataSource.php -text framework/Collections/TPagedList.php -text diff --git a/HISTORY b/HISTORY index 61fec720..b851d542 100644 --- a/HISTORY +++ b/HISTORY @@ -1,3 +1,28 @@ +Version 3.1.8 May ??, 2011 +BUG: Issue#226 - TTimerControl fires immediately (rojaro) +BUG: Issue#229 - Typo in TCachePageStatePersister.php (Christophe) +BUG: Issue#232 - Could not change enable-state of TActiveCheckBox via Ajax callback (Christophe) +BUG: Issue#236 - Bug in getIsView() in TOracleMetaData (rojaro) +BUG: Issue#239 - set_magic_quotes_runtime is deprecate since php 5.3.0 and will be removed in 6.0 (rojaro) +BUG: Issue#246 - TQueue::peek returns the top not the bottom (Christophe) +BUG: Issue#268 - String comparison with "=" does not work in Mysql 5.1 (rojaro) +BUG: Issue#273 - Typo in TURLMapping->getPatternMatches UrlFormat="Path" (rojaro) +BUG: Issue#276 - TListItemCollection never existed caused by Warning "PradoBase::include_once(TListItemCollection.php)" (rojaro) +BUG: Issue#277 - Inconsistence in setting 'day' value in TDatePicker (rojaro) +BUG: Issue#278 - Typo in file TActiveRecordRelation (rojaro) +BUG: Issue#285 - getPathOfNamespace returns cached namespace and ignores ext (rojaro) +BUG: Issue#286 - TBoundColumn->initializeCell bugs (rojaro) +BUG: Issue#287 - Inconsistent behavior in TSimpleDateFormatter->parse() (rojaro) +BUG: Issue#288 - Fixed two bugs in the Translation->init() method (rojaro) +BUG: Issue#294 - Better handling of missing array parameters in SQLMap (rojaro) +BUG: Issue#296 - URL tag not being parsed in component attributes (rojaro) +BUG: Issue#300 - Minor error in TErrorHandler.php (rojaro) + + + +ENH: Security manager: removed the zero byte right trim from the decryption routine and also made some cosmetic changes (rojaro) +ENH: Upgraded the phpunit ini file to work with PHPUnit 3.3 3.4 and 3.5. There are conditionals for 3.3 and 3.4. No more Framework file includes are needed with 3.5. (javalizard) + Version 3.1.7 February 22, 2010 ENH: Issue#24 - Specify needed fields on demand (Yves) BUG: Issue#80 - Inconsistencies in TRegularExpressionValidator (Christophe) diff --git a/framework/Collections/TListItemCollection.php b/framework/Collections/TListItemCollection.php new file mode 100644 index 00000000..d9adb161 --- /dev/null +++ b/framework/Collections/TListItemCollection.php @@ -0,0 +1,164 @@ + + * @author Qiang Xue + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2010 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TListControl.php 2624 2009-03-19 21:20:47Z godzilla80@gmx.net $ + * @package System.Collections + */ + +/** + * Includes the supporting classes + */ +Prado::using('System.Collections.TList'); +Prado::using('System.Web.UI.WebControls.TListItem'); + +/** + * TListItemCollection class. + * + * TListItemCollection maintains a list of {@link TListItem} for {@link TListControl}. + * + * @author Qiang Xue + * @version $Id: TListControl.php 2624 2009-03-19 21:20:47Z godzilla80@gmx.net $ + * @package System.Collections + * @since 3.0 + */ +class TListItemCollection extends TList +{ + /** + * Creates a list item object. + * This method may be overriden to provide a customized list item object. + * @param integer index where the newly created item is to be inserted at. + * If -1, the item will be appended to the end. + * @return TListItem list item object + */ + public function createListItem($index=-1) + { + $item=$this->createNewListItem(); + if($index<0) + $this->add($item); + else + $this->insertAt($index,$item); + return $item; + } + + /** + * @return TListItem new item. + */ + protected function createNewListItem($text=null) + { + $item = new TListItem; + if($text!==null) + $item->setText($text); + return $item; + } + + /** + * Inserts an item into the collection. + * @param integer the location where the item will be inserted. + * The current item at the place and the following ones will be moved backward. + * @param TListItem the item to be inserted. + * @throws TInvalidDataTypeException if the item being inserted is neither a string nor TListItem + */ + public function insertAt($index,$item) + { + if(is_string($item)) + $item = $this->createNewListItem($item); + if(!($item instanceof TListItem)) + throw new TInvalidDataTypeException('listitemcollection_item_invalid',get_class($this)); + parent::insertAt($index,$item); + } + + /** + * Finds the lowest cardinal index of the item whose value is the one being looked for. + * @param string the value to be looked for + * @param boolean whether to look for disabled items also + * @return integer the index of the item found, -1 if not found. + */ + public function findIndexByValue($value,$includeDisabled=true) + { + $value=TPropertyValue::ensureString($value); + $index=0; + foreach($this as $item) + { + if($item->getValue()===$value && ($includeDisabled || $item->getEnabled())) + return $index; + $index++; + } + return -1; + } + + /** + * Finds the lowest cardinal index of the item whose text is the one being looked for. + * @param string the text to be looked for + * @param boolean whether to look for disabled items also + * @return integer the index of the item found, -1 if not found. + */ + public function findIndexByText($text,$includeDisabled=true) + { + $text=TPropertyValue::ensureString($text); + $index=0; + foreach($this as $item) + { + if($item->getText()===$text && ($includeDisabled || $item->getEnabled())) + return $index; + $index++; + } + return -1; + } + + /** + * Finds the item whose value is the one being looked for. + * @param string the value to be looked for + * @param boolean whether to look for disabled items also + * @return TListItem the item found, null if not found. + */ + public function findItemByValue($value,$includeDisabled=true) + { + if(($index=$this->findIndexByValue($value,$includeDisabled))>=0) + return $this->itemAt($index); + else + return null; + } + + /** + * Finds the item whose text is the one being looked for. + * @param string the text to be looked for + * @param boolean whether to look for disabled items also + * @return TListItem the item found, null if not found. + */ + public function findItemByText($text,$includeDisabled=true) + { + if(($index=$this->findIndexByText($text,$includeDisabled))>=0) + return $this->itemAt($index); + else + return null; + } + + /** + * Loads state into every item in the collection. + * This method should only be used by framework and control developers. + * @param array|null state to be loaded. + */ + public function loadState($state) + { + $this->clear(); + if($state!==null) + $this->copyFrom($state); + } + + /** + * Saves state of items. + * This method should only be used by framework and control developers. + * @return array|null the saved state + */ + public function saveState() + { + return ($this->getCount()>0) ? $this->toArray() : null; + } +} diff --git a/framework/Collections/TQueue.php b/framework/Collections/TQueue.php index 1eacfb0a..581a7109 100644 --- a/framework/Collections/TQueue.php +++ b/framework/Collections/TQueue.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.Collections @@ -105,8 +105,8 @@ class TQueue extends TComponent implements IteratorAggregate,Countable } /** - * Returns the item at the top of the queue. - * Unlike {@link pop()}, this method does not remove the item from the queue. + * Returns the first item at the front of the queue. + * Unlike {@link dequeue()}, this method does not remove the item from the queue. * @return mixed item at the top of the queue * @throws TInvalidOperationException if the queue is empty */ @@ -115,7 +115,7 @@ class TQueue extends TComponent implements IteratorAggregate,Countable if($this->_c===0) throw new TInvalidOperationException('queue_empty'); else - return $this->_d[$this->_c-1]; + return $this->_d[0]; } /** diff --git a/framework/I18N/Translation.php b/framework/I18N/Translation.php index a0fa504d..8df36bc6 100644 --- a/framework/I18N/Translation.php +++ b/framework/I18N/Translation.php @@ -1,108 +1,107 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2008 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.I18N - */ - - /** - * Get the MessageFormat class. - */ -Prado::using('System.I18N.core.MessageFormat'); - - -/** - * Translation class. - * - * Provides translation using a static MessageFormatter. - * - * @author Xiang Wei Zhuo - * @version v1.0, last update on Tue Dec 28 11:54:48 EST 2004 - * @package System.I18N - */ -class Translation extends TComponent -{ - /** - * The array of formatters. We define 1 formatter per translation catalog - * This is a class static variable. - * @var array - */ - protected static $formatters=array(); - - /** - * Initialize the TTranslate translation components - */ - public static function init($catalogue='messages') - { + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.I18N + */ + +/** + * Get the MessageFormat class. + */ +Prado::using('System.I18N.core.MessageFormat'); + + +/** + * Translation class. + * + * Provides translation using a static MessageFormatter. + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Tue Dec 28 11:54:48 EST 2004 + * @package System.I18N + */ +class Translation extends TComponent +{ + /** + * The array of formatters. We define 1 formatter per translation catalog + * This is a class static variable. + * @var array + */ + protected static $formatters=array(); + + /** + * Initialize the TTranslate translation components + */ + public static function init($catalogue='messages') + { static $saveEventHandlerAttached=false; - //initialized the default class wide formatter - if(!isset(self::$formatters[$catalogue])) - { - $app = Prado::getApplication()->getGlobalization(); - $config = $app->getTranslationConfiguration(); - $source = MessageSource::factory($config['type'], - $config['source'], - $config['filename']); - - $source->setCulture($app->getCulture()); - - if($config['cache']) - $source->setCache(new MessageCache($config['cache'])); - - self::$formatters[$catalogue] = new MessageFormat($source, $app->getCharset()); - - //mark untranslated text - if($ps=$config['marker']) - self::$formatters[$catalogue]->setUntranslatedPS(array($ps,$ps)); - + //initialized the default class wide formatter + if(!isset(self::$formatters[$catalogue])) + { + $app = Prado::getApplication()->getGlobalization(); + $config = $app->getTranslationConfiguration(); + $source = MessageSource::factory($config['type'], + $config['source'], + $config['filename']); + + $source->setCulture($app->getCulture()); + + if(TPropertyValue::ensureBoolean($config['cache'])) + $source->setCache(new MessageCache($config['cache'])); + + self::$formatters[$catalogue] = new MessageFormat($source, $app->getCharset()); + + //mark untranslated text + if($ps=$config['marker']) + self::$formatters[$catalogue]->setUntranslatedPS(array($ps,$ps)); + //save the message on end request - // Do it only once ! - if (!$saveEventHandlerAttached) + // Do it only once ! + if(!$saveEventHandlerAttached && TPropertyValue::ensureBoolean($config['autosave'])) { - Prado::getApplication()->attachEventHandler( + Prado::getApplication()->attachEventHandler( 'OnEndRequest', array('Translation', 'saveMessages')); $saveEventHandlerAttached=true; - } - } - } - - /** - * Get the static formatter from this component. - * @return MessageFormat formattter. - * @see localize() - */ - public static function formatter($catalogue='messages') - { - return self::$formatters[$catalogue]; - } - - /** - * Save untranslated messages to the catalogue. - */ - public static function saveMessages() - { - static $onceonly = true; - - if($onceonly) - { - foreach (self::$formatters as $catalogue=>$formatter) - { - $app = Prado::getApplication()->getGlobalization(); - $config = $app->getTranslationConfiguration(); - if(isset($config['autosave'])) - { - $formatter->getSource()->setCulture($app->getCulture()); - $formatter->getSource()->save($catalogue); - } - } - $onceonly = false; - } - } -} - + } + } + } + + /** + * Get the static formatter from this component. + * @return MessageFormat formattter. + * @see localize() + */ + public static function formatter($catalogue='messages') + { + return self::$formatters[$catalogue]; + } + + /** + * Save untranslated messages to the catalogue. + */ + public static function saveMessages() + { + static $onceonly = true; + + if($onceonly) + { + foreach (self::$formatters as $catalogue=>$formatter) + { + $app = Prado::getApplication()->getGlobalization(); + $config = $app->getTranslationConfiguration(); + if(isset($config['autosave'])) + { + $formatter->getSource()->setCulture($app->getCulture()); + $formatter->getSource()->save($catalogue); + } + } + $onceonly = false; + } + } +} diff --git a/framework/PradoBase.php b/framework/PradoBase.php index 3a982ec5..222f492e 100644 --- a/framework/PradoBase.php +++ b/framework/PradoBase.php @@ -341,21 +341,24 @@ class PradoBase * @param string extension to be appended if the namespace refers to a file * @return string file path corresponding to the namespace, null if namespace is invalid */ - public static function getPathOfNamespace($namespace,$ext='') + 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 + if(self::CLASS_FILE_EXT === $ext || empty($ext)) { - $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; + if(isset(self::$_usings[$namespace])) + return self::$_usings[$namespace]; + + if(isset(self::$_aliases[$namespace])) + return self::$_aliases[$namespace]; } + + $segs = explode('.',$namespace); + $alias = array_shift($segs); + + if(null !== ($file = array_pop($segs)) && null !== ($root = self::getPathOfAlias($alias))) + return rtrim($root.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR ,$segs),'/\\').(($file === '*') ? '' : DIRECTORY_SEPARATOR.$file.$ext); + + return null; } /** diff --git a/framework/Security/TSecurityManager.php b/framework/Security/TSecurityManager.php index d43c9fec..18ced3c7 100644 --- a/framework/Security/TSecurityManager.php +++ b/framework/Security/TSecurityManager.php @@ -1,281 +1,268 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2008 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Security - */ - -/** - * TSecurityManager class - * - * TSecurityManager provides private keys, hashing and encryption - * functionalities that may be used by other PRADO components, - * such as viewstate persister, cookies. - * - * TSecurityManager is mainly used to protect data from being tampered - * and viewed. It can generate HMAC and encrypt the data. - * The private key used to generate HMAC is set by {@link setValidationKey ValidationKey}. - * The key used to encrypt data is specified by {@link setEncryptionKey EncryptionKey}. - * If the above keys are not explicitly set, random keys will be generated - * and used. - * - * To prefix data with an HMAC, call {@link hashData()}. - * To validate if data is tampered, call {@link validateData()}, which will - * return the real data if it is not tampered. - * The algorithm used to generated HMAC is specified by {@link setValidation Validation}. - * - * To encrypt and decrypt data, call {@link encrypt()} and {@link decrypt()} - * respectively. The encryption algorithm can be set by {@link setEncryption Encryption}. - * - * Note, to use encryption, the PHP Mcrypt extension must be loaded. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Security - * @since 3.0 - */ -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'; - - /** - * Initializes the module. - * The security module is registered with the application. - * @param TXmlElement initial module configuration - */ - public function init($config) - { - $this->getApplication()->setSecurityManager($this); - } - - /** - * Generates a random key. - */ - protected function generateRandomKey() - { - return rand().rand().rand().rand(); - } - - /** - * @return string the private key used to generate HMAC. - * If the key is not explicitly set, a random one is generated and returned. - */ - 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; - } - - /** - * @param string the key used to generate HMAC - * @throws TInvalidDataValueException if the key is empty - */ - public function setValidationKey($value) - { - if($value!=='') - $this->_validationKey=$value; - else - throw new TInvalidDataValueException('securitymanager_validationkey_invalid'); - } - - /** - * @return string the private key used to encrypt/decrypt data. - * If the key is not explicitly set, a random one is generated and returned. - */ - 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; - } - - /** - * @param string the key used to encrypt/decrypt data. - * @throws TInvalidDataValueException if the key is empty - */ - public function setEncryptionKey($value) - { - if($value!=='') - $this->_encryptionKey=$value; - else - throw new TInvalidDataValueException('securitymanager_encryptionkey_invalid'); - } - - /** - * @return TSecurityManagerValidationMode hashing algorithm used to generate HMAC. Defaults to TSecurityManagerValidationMode::SHA1. - */ - public function getValidation() - { - return $this->_validation; - } - - /** - * @param TSecurityManagerValidationMode hashing algorithm used to generate HMAC. - */ - public function setValidation($value) - { - $this->_validation=TPropertyValue::ensureEnum($value,'TSecurityManagerValidationMode'); - } - - /** - * @return string the algorithm used to encrypt/decrypt data. Defaults to '3DES'. - */ - public function getEncryption() - { - return $this->_encryption; - } - - /** - * @throws TNotSupportedException Do not call this method presently. - */ - public function setEncryption($value) - { - throw new TNotSupportedException('Currently only 3DES encryption is supported'); - } - - /** - * Encrypts data with {@link getEncryptionKey EncryptionKey}. - * @param string data to be encrypted. - * @return string the encrypted data - * @throws TNotSupportedException if PHP Mcrypt extension is not loaded - */ - 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'); - } - - /** - * Decrypts data with {@link getEncryptionKey EncryptionKey}. - * @param string data to be decrypted. - * @return string the decrypted data - * @throws TNotSupportedException if PHP Mcrypt extension is not loaded - */ - 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'); - } - - /** - * Prefixes data with an HMAC. - * @param string data to be hashed. - * @return string data prefixed with HMAC - */ - public function hashData($data) - { - $hmac=$this->computeHMAC($data); - return $hmac.$data; - } - - /** - * Validates if data is tampered. - * @param string data to be validated. The data must be previously - * generated using {@link hashData()}. - * @return string the real data with HMAC stripped off. False if the data - * is tampered. - */ - 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; - } - - /** - * Computes the HMAC for the data with {@link getValidationKey ValidationKey}. - * @param string data to be generated HMAC - * @return string the HMAC for the data - */ - 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))); - } -} - - -/** - * TSecurityManagerValidationMode class. - * TSecurityManagerValidationMode defines the enumerable type for the possible validation modes - * that can be used by {@link TSecurityManager}. - * - * The following enumerable values are defined: - * - MD5: an MD5 hash is generated from the data and used for validation. - * - SHA1: an SHA1 hash is generated from the data and used for validation. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Security - * @since 3.0.4 - */ -class TSecurityManagerValidationMode extends TEnumerable -{ - const MD5='MD5'; - const SHA1='SHA1'; -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Security + */ + +/** + * TSecurityManager class + * + * TSecurityManager provides private keys, hashing and encryption + * functionalities that may be used by other PRADO components, + * such as viewstate persister, cookies. + * + * TSecurityManager is mainly used to protect data from being tampered + * and viewed. It can generate HMAC and encrypt the data. + * The private key used to generate HMAC is set by {@link setValidationKey ValidationKey}. + * The key used to encrypt data is specified by {@link setEncryptionKey EncryptionKey}. + * If the above keys are not explicitly set, random keys will be generated + * and used. + * + * To prefix data with an HMAC, call {@link hashData()}. + * To validate if data is tampered, call {@link validateData()}, which will + * return the real data if it is not tampered. + * The algorithm used to generated HMAC is specified by {@link setValidation Validation}. + * + * To encrypt and decrypt data, call {@link encrypt()} and {@link decrypt()} + * respectively. The encryption algorithm can be set by {@link setEncryption Encryption}. + * + * Note, to use encryption, the PHP Mcrypt extension must be loaded. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Security + * @since 3.0 + */ +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'; + + /** + * Initializes the module. + * The security module is registered with the application. + * @param TXmlElement initial module configuration + */ + public function init($config) + { + $this->getApplication()->setSecurityManager($this); + } + + /** + * Generates a random key. + */ + protected function generateRandomKey() + { + return rand().rand().rand().rand(); + } + + /** + * @return string the private key used to generate HMAC. + * If the key is not explicitly set, a random one is generated and returned. + */ + public function getValidationKey() + { + if(null === $this->_validationKey) { + if(null === ($this->_validationKey = $this->getApplication()->getGlobalState(self::STATE_VALIDATION_KEY))) { + $this->_validationKey = $this->generateRandomKey(); + $this->getApplication()->setGlobalState(self::STATE_VALIDATION_KEY, $this->_validationKey, null); + } + } + return $this->_validationKey; + } + + /** + * @param string the key used to generate HMAC + * @throws TInvalidDataValueException if the key is empty + */ + public function setValidationKey($value) + { + if('' === $value) + throw new TInvalidDataValueException('securitymanager_validationkey_invalid'); + + $this->_validationKey = $value; + } + + /** + * @return string the private key used to encrypt/decrypt data. + * If the key is not explicitly set, a random one is generated and returned. + */ + public function getEncryptionKey() + { + if(null === $this->_encryptionKey) { + if(null === ($this->_encryptionKey = $this->getApplication()->getGlobalState(self::STATE_ENCRYPTION_KEY))) { + $this->_encryptionKey = $this->generateRandomKey(); + $this->getApplication()->setGlobalState(self::STATE_ENCRYPTION_KEY, $this->_encryptionKey, null); + } + } + return $this->_encryptionKey; + } + + /** + * @param string the key used to encrypt/decrypt data. + * @throws TInvalidDataValueException if the key is empty + */ + public function setEncryptionKey($value) + { + if('' === $value) + throw new TInvalidDataValueException('securitymanager_encryptionkey_invalid'); + + $this->_encryptionKey = $value; + } + + /** + * @return TSecurityManagerValidationMode hashing algorithm used to generate HMAC. Defaults to TSecurityManagerValidationMode::SHA1. + */ + public function getValidation() + { + return $this->_validation; + } + + /** + * @param TSecurityManagerValidationMode hashing algorithm used to generate HMAC. + */ + public function setValidation($value) + { + $this->_validation = TPropertyValue::ensureEnum($value, 'TSecurityManagerValidationMode'); + } + + /** + * @return string the algorithm used to encrypt/decrypt data. Defaults to '3DES'. + */ + public function getEncryption() + { + return $this->_encryption; + } + + /** + * @throws TNotSupportedException Do not call this method presently. + */ + public function setEncryption($value) + { + throw new TNotSupportedException('Currently only 3DES encryption is supported'); + } + + /** + * Encrypts data with {@link getEncryptionKey EncryptionKey}. + * @param string data to be encrypted. + * @return string the encrypted data + * @throws TNotSupportedException if PHP Mcrypt extension is not loaded + */ + public function encrypt($data) + { + if(!function_exists('mcrypt_encrypt')) + throw new TNotSupportedException('securitymanager_mcryptextension_required'); + + $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; + } + + /** + * Decrypts data with {@link getEncryptionKey EncryptionKey}. + * @param string data to be decrypted. + * @return string the decrypted data + * @throws TNotSupportedException if PHP Mcrypt extension is not loaded + */ + public function decrypt($data) + { + if(!function_exists('mcrypt_decrypt')) + throw new TNotSupportedException('securitymanager_mcryptextension_required'); + + $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 $decrypted; + } + + /** + * Prefixes data with an HMAC. + * @param string data to be hashed. + * @return string data prefixed with HMAC + */ + public function hashData($data) + { + $hmac = $this->computeHMAC($data); + return $hmac.$data; + } + + /** + * Validates if data is tampered. + * @param string data to be validated. The data must be previously + * generated using {@link hashData()}. + * @return string the real data with HMAC stripped off. False if the data + * is tampered. + */ + public function validateData($data) + { + $len = 'SHA1' === $this->_validation ? 40 : 32; + if(strlen($data) < $len) + return false; + + $hmac = substr($data, 0, $len); + $data2 = substr($data, $len); + return $hmac === $this->computeHMAC($data2) ? $data2 : false; + } + + /** + * Computes the HMAC for the data with {@link getValidationKey ValidationKey}. + * @param string data to be generated HMAC + * @return string the HMAC for the data + */ + protected function computeHMAC($data) + { + if('SHA1' === $this->_validation) { + $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))); + } +} + +/** + * TSecurityManagerValidationMode class. + * TSecurityManagerValidationMode defines the enumerable type for the possible validation modes + * that can be used by {@link TSecurityManager}. + * + * The following enumerable values are defined: + * - MD5: an MD5 hash is generated from the data and used for validation. + * - SHA1: an SHA1 hash is generated from the data and used for validation. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Security + * @since 3.0.4 + */ +class TSecurityManagerValidationMode extends TEnumerable +{ + const MD5 = 'MD5'; + const SHA1 = 'SHA1'; +} diff --git a/framework/Util/TSimpleDateFormatter.php b/framework/Util/TSimpleDateFormatter.php index 07a59398..15842f27 100644 --- a/framework/Util/TSimpleDateFormatter.php +++ b/framework/Util/TSimpleDateFormatter.php @@ -301,7 +301,8 @@ class TSimpleDateFormatter } } if ($i_val != $this->length($value)) - throw new TInvalidDataValueException("Pattern '{$this->pattern}' mismatch", $value); + return null; + //throw new TInvalidDataValueException("Pattern '{$this->pattern}' mismatch", $value); if(!$defaultToCurrentTime && ($month === null || $day === null || $year === null)) return null; else diff --git a/framework/Web/Javascripts/source/prado/activecontrols/activecontrols3.js b/framework/Web/Javascripts/source/prado/activecontrols/activecontrols3.js index 56395d44..97bd4e07 100644 --- a/framework/Web/Javascripts/source/prado/activecontrols/activecontrols3.js +++ b/framework/Web/Javascripts/source/prado/activecontrols/activecontrols3.js @@ -188,7 +188,6 @@ Prado.WebUI.TTimeTriggeredCallback = Base.extend( startTimer : function() { - setTimeout(this.onTimerEvent.bind(this), 100); if(typeof(this.timer) == 'undefined' || this.timer == null) this.timer = setInterval(this.onTimerEvent.bind(this),this.options.Interval*1000); }, diff --git a/framework/Web/TUrlMapping.php b/framework/Web/TUrlMapping.php index 47232401..b95779fe 100644 --- a/framework/Web/TUrlMapping.php +++ b/framework/Web/TUrlMapping.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 @@ -264,7 +264,7 @@ class TUrlMapping extends TUrlManager if(is_string($key)) $params[$key]=$value; } - if (!$pattern->getIsWildCardPattern()) + if (!$pattern->getIsWildCardPattern()) $params[$pattern->getServiceID()]=$pattern->getServiceParameter(); return $params; } @@ -300,8 +300,8 @@ class TUrlMapping extends TUrlManager if(!(is_array($getItems) || ($getItems instanceof Traversable))) $getItems=array(); $key=$serviceID.':'.$serviceParam; - $wildCardKey = ($pos=strrpos($serviceParam,'.'))!==false ? - $serviceID.':'.substr($serviceParam,0,$pos).'.*' : $serviceID.':*'; + $wildCardKey = ($pos=strrpos($serviceParam,'.'))!==false ? + $serviceID.':'.substr($serviceParam,0,$pos).'.*' : $serviceID.':*'; if(isset($this->_constructRules[$key])) { foreach($this->_constructRules[$key] as $rule) @@ -309,16 +309,16 @@ class TUrlMapping extends TUrlManager if($rule->supportCustomUrl($getItems)) return $rule->constructUrl($getItems,$encodeAmpersand,$encodeGetItems); } - } - elseif(isset($this->_constructRules[$wildCardKey])) - { + } + elseif(isset($this->_constructRules[$wildCardKey])) + { foreach($this->_constructRules[$wildCardKey] as $rule) { if($rule->supportCustomUrl($getItems)) - { - $getItems['*']= $pos ? substr($serviceParam,$pos+1) : $serviceParam; + { + $getItems['*']= $pos ? substr($serviceParam,$pos+1) : $serviceParam; return $rule->constructUrl($getItems,$encodeAmpersand,$encodeGetItems); - } + } } } } @@ -380,42 +380,42 @@ class TUrlMapping extends TUrlManager * The {@link setServiceParameter ServiceParameter} and {@link setServiceID ServiceID} * (the default ID is 'page') set the service parameter and service id respectively. * - * Since 3.1.4 you can also use simplyfied wildcard patterns to match multiple - * ServiceParameters with a single rule. The pattern must contain the placeholder - * {*} for the ServiceParameter. For example - * - * - * - * This rule will match an URL like http://example.com/index.php/admin/edituser - * and resolve it to the page Application.pages.admin.edituser. The wildcard matching - * is non-recursive. That means you have to add a rule for every subdirectory you - * want to access pages in: - * - * - * - * It is still possible to define an explicit rule for a page in the wildcard path. - * This rule has to preceed the wildcard rule. - * - * You can also use parameters with wildcard patterns. The parameters are then - * available with every matching page: - * - * - * - * To enable automatic parameter encoding in a path format fro wildcard patterns you can set - * {@setUrlFormat UrlFormat} to 'Path': - * - * - * - * This will create and parse URLs of the form - * .../index.php/admin/listuser/param1/value1/param2/value2. - * - * Use {@setUrlParamSeparator} to define another separator character between parameter - * name and value. Parameter/value pairs are always separated by a '/'. - * - * - * - * .../index.php/admin/listuser/param1-value1/param2-value2. - * + * Since 3.1.4 you can also use simplyfied wildcard patterns to match multiple + * ServiceParameters with a single rule. The pattern must contain the placeholder + * {*} for the ServiceParameter. For example + * + * + * + * This rule will match an URL like http://example.com/index.php/admin/edituser + * and resolve it to the page Application.pages.admin.edituser. The wildcard matching + * is non-recursive. That means you have to add a rule for every subdirectory you + * want to access pages in: + * + * + * + * It is still possible to define an explicit rule for a page in the wildcard path. + * This rule has to preceed the wildcard rule. + * + * You can also use parameters with wildcard patterns. The parameters are then + * available with every matching page: + * + * + * + * To enable automatic parameter encoding in a path format fro wildcard patterns you can set + * {@setUrlFormat UrlFormat} to 'Path': + * + * + * + * This will create and parse URLs of the form + * .../index.php/admin/listuser/param1/value1/param2/value2. + * + * Use {@setUrlParamSeparator} to define another separator character between parameter + * name and value. Parameter/value pairs are always separated by a '/'. + * + * + * + * .../index.php/admin/listuser/param1-value1/param2-value2. + * * @author Wei Zhuo * @version $Id$ * @package System.Web @@ -449,12 +449,12 @@ class TUrlMappingPattern extends TComponent private $_manager; private $_caseSensitive=true; - - private $_isWildCardPattern=false; - - private $_urlFormat=THttpRequestUrlFormat::Get; - - private $_separator='/'; + + private $_isWildCardPattern=false; + + private $_urlFormat=THttpRequestUrlFormat::Get; + + private $_separator='/'; /** * Constructor. @@ -484,8 +484,8 @@ class TUrlMappingPattern extends TComponent { if($this->_serviceParameter===null) throw new TConfigurationException('urlmappingpattern_serviceparameter_required', $this->getPattern()); - if(strpos($this->_serviceParameter,'*')!==false) - $this->_isWildCardPattern=true; + if(strpos($this->_serviceParameter,'*')!==false) + $this->_isWildCardPattern=true; } /** @@ -502,19 +502,19 @@ class TUrlMappingPattern extends TComponent $params[]='{'.$key.'}'; $values[]='(?P<'.$key.'>'.$value.')'; } - if ($this->getIsWildCardPattern()) { - $params[]='{*}'; - // service parameter must not contain '=' and '/' - $values[]='(?P<'.$this->getServiceID().'>[^=/]+)'; - } + if ($this->getIsWildCardPattern()) { + $params[]='{*}'; + // service parameter must not contain '=' and '/' + $values[]='(?P<'.$this->getServiceID().'>[^=/]+)'; + } $params[]='/'; $values[]='\\/'; $regexp=str_replace($params,$values,trim($this->getPattern(),'/').'/'); - if ($this->_urlFormat===THttpRequestUrlFormat::Get) + if ($this->_urlFormat===THttpRequestUrlFormat::Get) $regexp='/^'.$regexp.'$/u'; - else + else $regexp='/^'.$regexp.'(?P.*)$/u'; - + if(!$this->getCaseSensitive()) $regexp.='i'; return $regexp; @@ -629,30 +629,30 @@ class TUrlMappingPattern extends TComponent preg_match($pattern,$request->getPathInfo(),$matches); else preg_match($this->getParameterizedPattern(),trim($request->getPathInfo(),'/').'/',$matches); - - if($this->getIsWildCardPattern() && isset($matches[$this->_serviceID])) - $matches[$this->_serviceID]=str_replace('*',$matches[$this->_serviceID],$this->_serviceParameter); - - if (isset($matches['urlparams'])) - { - $params=explode('/',$matches['urlparams']); - if ($this->_separator==='/') - { - while($key=array_shift($params)) - $matches2[$key]=($value=array_shift($params)) ? $value : ''; - } - else - { - array_pop($params); - foreach($params as $param) - { - list($key,$value)=explode($this->_separator,$param,2); - $matches[$key]=$value; - } - } - unset($matches['urlparams']); - } - + + if($this->getIsWildCardPattern() && isset($matches[$this->_serviceID])) + $matches[$this->_serviceID]=str_replace('*',$matches[$this->_serviceID],$this->_serviceParameter); + + if (isset($matches['urlparams'])) + { + $params=explode('/',$matches['urlparams']); + if ($this->_separator==='/') + { + while($key=array_shift($params)) + $matches[$key]=($value=array_shift($params)) ? $value : ''; + } + else + { + array_pop($params); + foreach($params as $param) + { + list($key,$value)=explode($this->_separator,$param,2); + $matches[$key]=$value; + } + } + unset($matches['urlparams']); + } + return $matches; } @@ -676,57 +676,57 @@ class TUrlMappingPattern extends TComponent } /** - * @return boolean whether this pattern is a wildcard pattern + * @return boolean whether this pattern is a wildcard pattern * @since 3.1.4 */ - public function getIsWildCardPattern() { - return $this->_isWildCardPattern; - } - - /** - * @return THttpRequestUrlFormat the format of URLs. Defaults to THttpRequestUrlFormat::Get. - */ - public function getUrlFormat() - { - return $this->_urlFormat; - } - - /** - * Sets the format of URLs constructed and interpreted by this pattern. - * A Get URL format is like index.php?name1=value1&name2=value2 - * while a Path URL format is like index.php/name1/value1/name2/value. - * The separating character between name and value can be configured with - * {@link setUrlParamSeparator} and defaults to '/'. - * Changing the UrlFormat will affect {@link constructUrl} and how GET variables - * are parsed. - * @param THttpRequestUrlFormat the format of URLs. - * @param since 3.1.4 - */ - public function setUrlFormat($value) - { - $this->_urlFormat=TPropertyValue::ensureEnum($value,'THttpRequestUrlFormat'); - } - - /** - * @return string separator used to separate GET variable name and value when URL format is Path. Defaults to slash '/'. - */ - public function getUrlParamSeparator() - { - return $this->_separator; - } - - /** - * @param string separator used to separate GET variable name and value when URL format is Path. - * @throws TInvalidDataValueException if the separator is not a single character - */ - public function setUrlParamSeparator($value) - { - if(strlen($value)===1) - $this->_separator=$value; - else - throw new TInvalidDataValueException('httprequest_separator_invalid'); - } - + public function getIsWildCardPattern() { + return $this->_isWildCardPattern; + } + + /** + * @return THttpRequestUrlFormat the format of URLs. Defaults to THttpRequestUrlFormat::Get. + */ + public function getUrlFormat() + { + return $this->_urlFormat; + } + + /** + * Sets the format of URLs constructed and interpreted by this pattern. + * A Get URL format is like index.php?name1=value1&name2=value2 + * while a Path URL format is like index.php/name1/value1/name2/value. + * The separating character between name and value can be configured with + * {@link setUrlParamSeparator} and defaults to '/'. + * Changing the UrlFormat will affect {@link constructUrl} and how GET variables + * are parsed. + * @param THttpRequestUrlFormat the format of URLs. + * @param since 3.1.4 + */ + public function setUrlFormat($value) + { + $this->_urlFormat=TPropertyValue::ensureEnum($value,'THttpRequestUrlFormat'); + } + + /** + * @return string separator used to separate GET variable name and value when URL format is Path. Defaults to slash '/'. + */ + public function getUrlParamSeparator() + { + return $this->_separator; + } + + /** + * @param string separator used to separate GET variable name and value when URL format is Path. + * @throws TInvalidDataValueException if the separator is not a single character + */ + public function setUrlParamSeparator($value) + { + if(strlen($value)===1) + $this->_separator=$value; + else + throw new TInvalidDataValueException('httprequest_separator_invalid'); + } + /** * @param array list of GET items to be put in the constructed URL * @return boolean whether this pattern IS the one for constructing the URL with the specified GET items. @@ -770,12 +770,12 @@ class TUrlMappingPattern extends TComponent // for the rest of the GET variables, put them in the query string if(count($extra)>0) { - if ($this->_urlFormat===THttpRequestUrlFormat::Path && $this->getIsWildCardPattern()) { - foreach ($extra as $name=>$value) - $url.='/'.$name.$this->_separator.($encodeGetItems?rawurlencode($value):$value); - return $url; - } - + if ($this->_urlFormat===THttpRequestUrlFormat::Path && $this->getIsWildCardPattern()) { + foreach ($extra as $name=>$value) + $url.='/'.$name.$this->_separator.($encodeGetItems?rawurlencode($value):$value); + return $url; + } + $url2=''; $amp=$encodeAmpersand?'&':'&'; if($encodeGetItems) diff --git a/framework/Web/UI/ActiveControls/TCallbackClientScript.php b/framework/Web/UI/ActiveControls/TCallbackClientScript.php index 8fbdd864..d35e89a0 100644 --- a/framework/Web/UI/ActiveControls/TCallbackClientScript.php +++ b/framework/Web/UI/ActiveControls/TCallbackClientScript.php @@ -164,7 +164,8 @@ class TCallbackClientScript extends TApplicationComponent */ public function setAttribute($control, $name, $value) { - if ($control instanceof ISurroundable) + // Attributes should be applied on Surrounding tag, except for 'disabled' attribute + if ($control instanceof ISurroundable && strtolower($name)!=='disabled') $control=$control->getSurroundingTagID(); $this->callClientFunction('Prado.Element.setAttribute',array($control, $name, $value)); } diff --git a/framework/Web/UI/TTemplateManager.php b/framework/Web/UI/TTemplateManager.php index 6d44d7d7..af88620c 100644 --- a/framework/Web/UI/TTemplateManager.php +++ b/framework/Web/UI/TTemplateManager.php @@ -890,15 +890,19 @@ class TTemplate extends TApplicationComponent implements ITemplate else return array(self::CONFIG_EXPRESSION,ltrim($expr,'.')); } - else if(preg_match('/\\s*(<%~.*?%>|<%\\$.*?%>|<%\\[.*?\\]%>)\\s*/msS',$value,$matches) && $matches[0]===$value) + else if(preg_match('/\\s*(<%~.*?%>|<%\\$.*?%>|<%\\[.*?\\]%>|<%\/.*?%>)\\s*/msS',$value,$matches) && $matches[0]===$value) { $value=$matches[1]; - if($value[2]==='~') // a URL + if($value[2]==='~') return array(self::CONFIG_ASSET,trim(substr($value,3,strlen($value)-5))); - else if($value[2]==='[') + elseif($value[2]==='[') return array(self::CONFIG_LOCALIZATION,trim(substr($value,3,strlen($value)-6))); - else if($value[2]==='$') + elseif($value[2]==='$') return array(self::CONFIG_PARAMETER,trim(substr($value,3,strlen($value)-5))); + elseif($value[2]==='/') { + $literal = trim(substr($value,3,strlen($value)-5)); + return array(self::CONFIG_EXPRESSION,"dirname(\$this->getApplication()->getRequest()->getApplicationUrl()).'/$literal'"); + } } else return $value; diff --git a/framework/Web/UI/WebControls/TBoundColumn.php b/framework/Web/UI/WebControls/TBoundColumn.php index fdcde9b2..098ffeef 100644 --- a/framework/Web/UI/WebControls/TBoundColumn.php +++ b/framework/Web/UI/WebControls/TBoundColumn.php @@ -197,6 +197,7 @@ class TBoundColumn extends TDataGridColumn $control->setItemType($item->getItemType()); } $cell->getControls()->add($control); + $cell->registerObject('EditControl',$control); } else { @@ -206,7 +207,20 @@ class TBoundColumn extends TDataGridColumn } } else - $control=$cell; + { + if(($classPath=$this->getItemRenderer())!=='') + { + $control=Prado::createComponent($classPath); + if($control instanceof IItemDataRenderer) + { + $control->setItemIndex($item->getItemIndex()); + $control->setItemType($item->getItemType()); + } + $cell->getControls()->add($control); + } + else + $control=$cell; + } $control->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); break; default: diff --git a/framework/Web/UI/WebControls/TDatePicker.php b/framework/Web/UI/WebControls/TDatePicker.php index e678b046..c81d8bc1 100644 --- a/framework/Web/UI/WebControls/TDatePicker.php +++ b/framework/Web/UI/WebControls/TDatePicker.php @@ -487,7 +487,7 @@ class TDatePicker extends TTextBox if(isset($values[$key.'$day'])) $day = intval($values[$key.'$day']); else - $day = 1; + $day = $date['mday']; if(isset($values[$key.'$month'])) $month = intval($values[$key.'$month']) + 1; @@ -961,4 +961,4 @@ class TDatePickerPositionMode extends TEnumerable { const Top='Top'; const Bottom='Bottom'; -} \ No newline at end of file +} diff --git a/framework/Web/UI/WebControls/TListControl.php b/framework/Web/UI/WebControls/TListControl.php index c69b387e..e0c25b9e 100644 --- a/framework/Web/UI/WebControls/TListControl.php +++ b/framework/Web/UI/WebControls/TListControl.php @@ -1,10 +1,12 @@ * @author Qiang Xue * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2010 PradoSoft * @license http://www.pradosoft.com/license/ * @version $Id$ * @package System.Web.UI.WebControls @@ -15,10 +17,10 @@ */ Prado::using('System.Web.UI.WebControls.TDataBoundControl'); Prado::using('System.Web.UI.WebControls.TListItem'); +Prado::using('System.Collections.TListItemCollection'); Prado::using('System.Collections.TAttributeCollection'); Prado::using('System.Util.TDataFieldAccessor'); - /** * TListControl class * @@ -878,151 +880,6 @@ abstract class TListControl extends TDataBoundControl implements IDataRenderer } } -/** - * TListItemCollection class. - * - * TListItemCollection maintains a list of {@link TListItem} for {@link TListControl}. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TListItemCollection extends TList -{ - /** - * Creates a list item object. - * This method may be overriden to provide a customized list item object. - * @param integer index where the newly created item is to be inserted at. - * If -1, the item will be appended to the end. - * @return TListItem list item object - */ - public function createListItem($index=-1) - { - $item=$this->createNewListItem(); - if($index<0) - $this->add($item); - else - $this->insertAt($index,$item); - return $item; - } - - /** - * @return TListItem new item. - */ - protected function createNewListItem($text=null) - { - $item = new TListItem; - if($text!==null) - $item->setText($text); - return $item; - } - - /** - * Inserts an item into the collection. - * @param integer the location where the item will be inserted. - * The current item at the place and the following ones will be moved backward. - * @param TListItem the item to be inserted. - * @throws TInvalidDataTypeException if the item being inserted is neither a string nor TListItem - */ - public function insertAt($index,$item) - { - if(is_string($item)) - $item = $this->createNewListItem($item); - if(!($item instanceof TListItem)) - throw new TInvalidDataTypeException('listitemcollection_item_invalid',get_class($this)); - parent::insertAt($index,$item); - } - - /** - * Finds the lowest cardinal index of the item whose value is the one being looked for. - * @param string the value to be looked for - * @param boolean whether to look for disabled items also - * @return integer the index of the item found, -1 if not found. - */ - public function findIndexByValue($value,$includeDisabled=true) - { - $value=TPropertyValue::ensureString($value); - $index=0; - foreach($this as $item) - { - if($item->getValue()===$value && ($includeDisabled || $item->getEnabled())) - return $index; - $index++; - } - return -1; - } - - /** - * Finds the lowest cardinal index of the item whose text is the one being looked for. - * @param string the text to be looked for - * @param boolean whether to look for disabled items also - * @return integer the index of the item found, -1 if not found. - */ - public function findIndexByText($text,$includeDisabled=true) - { - $text=TPropertyValue::ensureString($text); - $index=0; - foreach($this as $item) - { - if($item->getText()===$text && ($includeDisabled || $item->getEnabled())) - return $index; - $index++; - } - return -1; - } - - /** - * Finds the item whose value is the one being looked for. - * @param string the value to be looked for - * @param boolean whether to look for disabled items also - * @return TListItem the item found, null if not found. - */ - public function findItemByValue($value,$includeDisabled=true) - { - if(($index=$this->findIndexByValue($value,$includeDisabled))>=0) - return $this->itemAt($index); - else - return null; - } - - /** - * Finds the item whose text is the one being looked for. - * @param string the text to be looked for - * @param boolean whether to look for disabled items also - * @return TListItem the item found, null if not found. - */ - public function findItemByText($text,$includeDisabled=true) - { - if(($index=$this->findIndexByText($text,$includeDisabled))>=0) - return $this->itemAt($index); - else - return null; - } - - /** - * Loads state into every item in the collection. - * This method should only be used by framework and control developers. - * @param array|null state to be loaded. - */ - public function loadState($state) - { - $this->clear(); - if($state!==null) - $this->copyFrom($state); - } - - /** - * Saves state of items. - * This method should only be used by framework and control developers. - * @return array|null the saved state - */ - public function saveState() - { - return ($this->getCount()>0) ? $this->toArray() : null; - } -} - /** * IListControlAdapter interface * diff --git a/tests/unit/phpunit.php b/tests/unit/phpunit.php index 8395c7a0..6d612e46 100644 --- a/tests/unit/phpunit.php +++ b/tests/unit/phpunit.php @@ -15,9 +15,21 @@ set_include_path(PRADO_FRAMEWORK_DIR.PATH_SEPARATOR.get_include_path()); require_once dirname(__FILE__).'/Prado.php'; require_once PRADO_FRAMEWORK_DIR.'/prado.php'; -require_once 'PHPUnit/Framework/TestCase.php'; -require_once 'PHPUnit/Framework/IncompleteTestError.php'; -require_once 'PHPUnit/Framework/TestSuite.php'; + +//grab the version of phpunit, OPP: phpunit provided version call +$version = `{$_SERVER['argv'][0]} --version`; +preg_match('/(\d\d?)\.(\d\d?)(\.(\d\d?))?/', $version, $matches); + +if($matches[1] <= 3){ + if($matches[2] <= 4){ + require_once 'PHPUnit/Framework.php'; + if($matches[2] <= 3){ + require_once 'PHPUnit/Framework/TestCase.php'; + require_once 'PHPUnit/Framework/IncompleteTestError.php'; + require_once 'PHPUnit/Framework/TestSuite.php'; + } + } +} require_once 'PHPUnit/TextUI/TestRunner.php'; require_once 'PHPUnit/Util/Filter.php'; ?> -- cgit v1.2.3