diff --git a/CHANGELOG b/CHANGELOG
index 812fa3295d..52918ce241 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,8 +2,8 @@
Yii Framework Change Log
========================
-Version 1.1.3 to be released
-----------------------------
+Version 1.1.3 July 4, 2010
+--------------------------
- Bug #856: Logout doesn't work when CWebUser::identityCookie is configured and allowAutoLogin is set true (Qiang)
- Bug #1027: CButtonColumn->buttons is ignored (Sam Dark)
- Bug #1039: Table prefix feature did not work with PostgreSQL and AR (Qiang)
@@ -39,6 +39,7 @@ Version 1.1.3 to be released
- Enh #1022: Added CMenu::activateItems (Qiang)
- Enh #1041: Added support to allow skinning pagers used in CGridView and CListView (Qiang)
- Enh #1043: Improved view resolution to support using themeable application views in a module (Qiang)
+- Enh #1049: Enhanced label generation when using CDetailView with associative arrays (Qiang)
- Enh #1127: Added support to automatically generate maxlength attribute for text/password inputs based on model rules (Qiang)
- Enh #1151: Added support to generate grid column header based on attribute names (Qiang)
- Enh #1158: Added translations in Latvian (lafriks)
@@ -52,6 +53,7 @@ Version 1.1.3 to be released
- Enh #1199: AR's count() method now respects the 'select' option in the query criteria (Qiang)
- Enh #1202: Added support for using anonymous functions as component property values (Qiang)
- Enh #1203: Gii now respects the newDirMode and newFileMode settings even when lower umask is set (Qiang)
+- Enh #1210: Added support to generate proper labels for relational properties in CDetailView (Qiang)
- Enh #1225: Added 'firstError' option to CHtml::errorSummary() to support displaying only the first error message of each model attribute (Qiang)
- Enh #1232: Added CAuthManager::showErrors. When value is true Yii will turn on error_reporting for RBAC bizRules. False by default (Sam Dark)
- Enh #1239: CBreadcrumbs should have the 'Home' label translated (Qiang)
@@ -78,6 +80,7 @@ Version 1.1.3 to be released
- Enh: Enhanced CSort::defaultOrder to allow using virtual attribute names (Qiang)
- Chg #1323: Conditions declared in scopes of the related AR classes will be put in the ON clause of the JOIN statement (Qiang)
- Chg: CAutoComplete is now deprecated (Sam Dark)
+- New: Added CJuiButton (sebas)
Version 1.1.2 May 2, 2010
-------------------------
diff --git a/framework/YiiBase.php b/framework/YiiBase.php
index dd3dc7fa4d..fc8d882cc7 100644
--- a/framework/YiiBase.php
+++ b/framework/YiiBase.php
@@ -68,7 +68,7 @@ class YiiBase
*/
public static function getVersion()
{
- return '1.1.3-dev';
+ return '1.1.3';
}
/**
diff --git a/framework/yiilite.php b/framework/yiilite.php
index eb6fa17b8a..0c111f9a71 100644
--- a/framework/yiilite.php
+++ b/framework/yiilite.php
@@ -39,7 +39,7 @@ class YiiBase
private static $_logger;
public static function getVersion()
{
- return '1.1.2-dev';
+ return '1.1.3';
}
public static function createWebApplication($config=null)
{
@@ -541,8 +541,17 @@ public function __isset($name)
$name=strtolower($name);
return isset($this->_e[$name]) && $this->_e[$name]->getCount();
}
- else
- return false;
+ else if(is_array($this->_m))
+ {
+ if(isset($this->_m[$name]))
+ return true;
+ foreach($this->_m as $object)
+ {
+ if($object->getEnabled() && (property_exists($object,$name) || $object->canGetProperty($name)))
+ return true;
+ }
+ }
+ return false;
}
public function __unset($name)
{
@@ -551,6 +560,24 @@ public function __unset($name)
$this->$setter(null);
else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
unset($this->_e[strtolower($name)]);
+ else if(is_array($this->_m))
+ {
+ if(isset($this->_m[$name]))
+ $this->detachBehavior($name);
+ else
+ {
+ foreach($this->_m as $object)
+ {
+ if($object->getEnabled())
+ {
+ if(property_exists($object,$name))
+ return $object->$name=null;
+ else if($object->canSetProperty($name))
+ return $object->$setter(null);
+ }
+ }
+ }
+ }
else if(method_exists($this,'get'.$name))
throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.',
array('{class}'=>get_class($this), '{property}'=>$name)));
@@ -565,6 +592,8 @@ public function __call($name,$parameters)
return call_user_func_array(array($object,$name),$parameters);
}
}
+ if(class_exists('Closure', false) && $this->$name instanceof Closure)
+ return call_user_func_array($this->$name, $parameters);
throw new CException(Yii::t('yii','{class} does not have a method named "{name}".',
array('{class}'=>get_class($this), '{name}'=>$name)));
}
@@ -931,9 +960,12 @@ public function setComponent($id,$component)
if(!$component->getIsInitialized())
$component->init();
}
- public function getComponents()
+ public function getComponents($loadedOnly=true)
{
- return $this->_components;
+ if($loadedOnly)
+ return $this->_components;
+ else
+ return array_merge($this->_componentConfig, $this->_components);
}
public function setComponents($components)
{
@@ -1418,6 +1450,9 @@ protected function registerCoreComponents()
'clientScript'=>array(
'class'=>'CClientScript',
),
+ 'widgetFactory'=>array(
+ 'class'=>'CWidgetFactory',
+ ),
);
$this->setComponents($components);
}
@@ -2048,13 +2083,24 @@ public function getHostInfo($schema='')
else
{
$this->_hostInfo=$http.'://'.$_SERVER['SERVER_NAME'];
- $port=$_SERVER['SERVER_PORT'];
- if(($port!=80 && !$secure) || ($port!=443 && $secure))
+ $port=$secure ? $this->getSecurePort() : $this->getPort();
+ if(($port!==80 && !$secure) || ($port!==443 && $secure))
$this->_hostInfo.=':'.$port;
}
}
- if($schema!=='' && ($pos=strpos($this->_hostInfo,':'))!==false)
- return $schema.substr($this->_hostInfo,$pos);
+ if($schema!=='')
+ {
+ $secure=$this->getIsSecureConnection();
+ if($secure && $schema==='https' || !$secure && $schema==='http')
+ return $this->_hostInfo;
+ $port=$schema==='https' ? $this->getSecurePort() : $this->getPort();
+ if($port!==80 && $schema==='http' || $port!==443 && $schema==='https')
+ $port=':'.$port;
+ else
+ $port='';
+ $pos=strpos($this->_hostInfo,':');
+ return $schema.substr($this->_hostInfo,$pos,strcspn($this->_hostInfo,':',$pos+1)+1).$port;
+ }
else
return $this->_hostInfo;
}
@@ -2199,6 +2245,30 @@ public function getAcceptTypes()
{
return $_SERVER['HTTP_ACCEPT'];
}
+ private $_port;
+ public function getPort()
+ {
+ if($this->_port===null)
+ $this->_port=!$this->getIsSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int)$_SERVER['SERVER_PORT'] : 80;
+ return $this->_port;
+ }
+ public function setPort($value)
+ {
+ $this->_port=(int)$value;
+ $this->_hostInfo=null;
+ }
+ private $_securePort;
+ public function getSecurePort()
+ {
+ if($this->_securePort===null)
+ $this->_securePort=$this->getIsSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int)$_SERVER['SERVER_PORT'] : 443;
+ return $this->_securePort;
+ }
+ public function setSecurePort($value)
+ {
+ $this->_securePort=(int)$value;
+ $this->_hostInfo=null;
+ }
public function getCookies()
{
if($this->_cookies!==null)
@@ -2647,6 +2717,13 @@ public function createUrl($manager,$route,$params,$ampersand)
else
return false;
}
+ foreach($this->defaultParams as $key=>$value)
+ {
+ if(isset($params[$key]) && $params[$key]==$value)
+ unset($params[$key]);
+ else
+ return false;
+ }
foreach($this->params as $key=>$value)
if(!isset($params[$key]))
return false;
@@ -2665,6 +2742,12 @@ public function createUrl($manager,$route,$params,$ampersand)
}
$suffix=$this->urlSuffix===null ? $manager->urlSuffix : $this->urlSuffix;
$url=strtr($this->template,$tr);
+ if($this->hasHostInfo)
+ {
+ $hostInfo=Yii::app()->getRequest()->getHostInfo();
+ if(strpos($url,$hostInfo)===0)
+ $url=substr($url,strlen($hostInfo));
+ }
if(empty($params))
return $url!=='' ? $url.$suffix : $url;
if($this->append)
@@ -2760,15 +2843,7 @@ public function renderInternal($_viewFile_,$_data_=null,$_return_=false)
}
public function createWidget($className,$properties=array())
{
- if(($factory=Yii::app()->getWidgetFactory())!==null)
- $widget=$factory->createWidget($this,$className,$properties);
- else
- {
- $className=Yii::import($className,true);
- $widget=new $className($this);
- foreach($properties as $name=>$value)
- $widget->$name=$value;
- }
+ $widget=Yii::app()->getWidgetFactory()->createWidget($this,$className,$properties);
$widget->init();
return $widget;
}
@@ -3026,9 +3101,10 @@ public function getViewFile($viewName)
{
if(($theme=Yii::app()->getTheme())!==null && ($viewFile=$theme->getViewFile($this,$viewName))!==false)
return $viewFile;
- $module=$this->getModule();
- $basePath=$module ? $module->getViewPath() : Yii::app()->getViewPath();
- return $this->resolveViewFile($viewName,$this->getViewPath(),$basePath);
+ $moduleViewPath=$basePath=Yii::app()->getViewPath();
+ if(($module=$this->getModule())!==null)
+ $moduleViewPath=$module->getViewPath();
+ return $this->resolveViewFile($viewName,$this->getViewPath(),$basePath,$moduleViewPath);
}
public function getLayoutFile($layoutName)
{
@@ -3049,25 +3125,29 @@ public function getLayoutFile($layoutName)
}
if($module===null)
$module=Yii::app();
- return $this->resolveViewFile($module->layout,$module->getLayoutPath(),$module->getViewPath());
- }
- else
- {
- if(($module=$this->getModule())===null)
- $module=Yii::app();
- return $this->resolveViewFile($layoutName,$module->getLayoutPath(),$module->getViewPath());
+ $layoutName=$module->layout;
}
+ else if(($module=$this->getModule())===null)
+ $module=Yii::app();
+ return $this->resolveViewFile($layoutName,$module->getLayoutPath(),Yii::app()->getViewPath(),$module->getViewPath());
}
- public function resolveViewFile($viewName,$viewPath,$basePath)
+ public function resolveViewFile($viewName,$viewPath,$basePath,$moduleViewPath=null)
{
if(empty($viewName))
return false;
+ if($moduleViewPath===null)
+ $moduleViewPath=$basePath;
if(($renderer=Yii::app()->getViewRenderer())!==null)
$extension=$renderer->fileExtension;
else
$extension='.php';
if($viewName[0]==='/')
- $viewFile=$basePath.$viewName;
+ {
+ if(strncmp($viewName,'//',2)===0)
+ $viewFile=$basePath.$viewName;
+ else
+ $viewFile=$moduleViewPath.$viewName;
+ }
else if(strpos($viewName,'.'))
$viewFile=Yii::getPathOfAlias($viewName);
else
@@ -3365,28 +3445,49 @@ public function init()
Yii::app()->getSession()->open();
if($this->getIsGuest() && $this->allowAutoLogin)
$this->restoreFromCookie();
+ else if($this->autoRenewCookie && $this->allowAutoLogin)
+ $this->renewCookie();
$this->updateFlash();
}
public function login($identity,$duration=0)
{
- $this->changeIdentity($identity->getId(),$identity->getName(),$identity->getPersistentStates());
- if($duration>0)
+ $id=$identity->getId();
+ $states=$identity->getPersistentStates();
+ if($this->beforeLogin($id,$states,false))
{
- if($this->allowAutoLogin)
- $this->saveToCookie($duration);
- else
- throw new CException(Yii::t('yii','{class}.allowAutoLogin must be set true in order to use cookie-based authentication.',
- array('{class}'=>get_class($this))));
+ $this->changeIdentity($id,$identity->getName(),$states);
+ if($duration>0)
+ {
+ if($this->allowAutoLogin)
+ $this->saveToCookie($duration);
+ else
+ throw new CException(Yii::t('yii','{class}.allowAutoLogin must be set true in order to use cookie-based authentication.',
+ array('{class}'=>get_class($this))));
+ }
+ $this->afterLogin(false);
}
}
public function logout($destroySession=true)
{
- if($this->allowAutoLogin)
- Yii::app()->getRequest()->getCookies()->remove($this->getStateKeyPrefix());
- if($destroySession)
- Yii::app()->getSession()->destroy();
- else
- $this->clearStates();
+ if($this->beforeLogout())
+ {
+ if($this->allowAutoLogin)
+ {
+ Yii::app()->getRequest()->getCookies()->remove($this->getStateKeyPrefix());
+ if($this->identityCookie!==null)
+ {
+ $cookie=$this->createIdentityCookie($this->getStateKeyPrefix());
+ $cookie->value=null;
+ $cookie->expire=0;
+ Yii::app()->getRequest()->getCookies()->add($cookie->name,$cookie);
+ }
+ }
+ if($destroySession)
+ Yii::app()->getSession()->destroy();
+ else
+ $this->clearStates();
+ $this->afterLogout();
+ }
}
public function getIsGuest()
{
@@ -3437,6 +3538,20 @@ public function loginRequired()
else
throw new CHttpException(403,Yii::t('yii','Login Required'));
}
+ protected function beforeLogin($id,$states,$fromCookie)
+ {
+ return true;
+ }
+ protected function afterLogin($fromCookie)
+ {
+ }
+ protected function beforeLogout()
+ {
+ return true;
+ }
+ protected function afterLogout()
+ {
+ }
protected function restoreFromCookie()
{
$app=Yii::app();
@@ -3447,15 +3562,33 @@ protected function restoreFromCookie()
if(is_array($data) && isset($data[0],$data[1],$data[2],$data[3]))
{
list($id,$name,$duration,$states)=$data;
- $this->changeIdentity($id,$name,$states);
- if($this->autoRenewCookie)
+ if($this->beforeLogin($id,$states,true))
{
- $cookie->expire=time()+$duration;
- $app->getRequest()->getCookies()->add($cookie->name,$cookie);
+ $this->changeIdentity($id,$name,$states);
+ if($this->autoRenewCookie)
+ {
+ $cookie->expire=time()+$duration;
+ $app->getRequest()->getCookies()->add($cookie->name,$cookie);
+ }
+ $this->afterLogin(true);
}
}
}
}
+ protected function renewCookie()
+ {
+ $cookies=Yii::app()->getRequest()->getCookies();
+ $cookie=$cookies->itemAt($this->getStateKeyPrefix());
+ if($cookie && !empty($cookie->value) && ($data=Yii::app()->getSecurityManager()->validateData($cookie->value))!==false)
+ {
+ $data=@unserialize($data);
+ if(is_array($data) && isset($data[0],$data[1],$data[2],$data[3]))
+ {
+ $cookie->expire=time()+$data[2];
+ $cookies->add($cookie->name,$cookie);
+ }
+ }
+ }
protected function saveToCookie($duration)
{
$app=Yii::app();
@@ -3520,6 +3653,25 @@ public function clearStates()
unset($_SESSION[$key]);
}
}
+ public function getFlashes($delete=true)
+ {
+ $flashes=array();
+ $prefix=$this->getStateKeyPrefix().self::FLASH_KEY_PREFIX;
+ $keys=array_keys($_SESSION);
+ $n=strlen($prefix);
+ foreach($keys as $key)
+ {
+ if(!strncmp($key,$prefix,$n))
+ {
+ $flashes[substr($key,$n)]=$_SESSION[$key];
+ if($delete)
+ unset($_SESSION[$key]);
+ }
+ }
+ if($delete)
+ $this->setState(self::FLASH_COUNTERS,array());
+ return $flashes;
+ }
public function getFlash($key,$defaultValue=null,$delete=true)
{
$value=$this->getState(self::FLASH_KEY_PREFIX.$key,$defaultValue);
@@ -3939,7 +4091,7 @@ public static function beginForm($action='',$method='post',$htmlOptions=array())
}
}
$request=Yii::app()->request;
- if($request->enableCsrfValidation)
+ if($request->enableCsrfValidation && !strcasecmp($method,'post'))
$hiddens[]=self::hiddenField($request->csrfTokenName,$request->getCsrfToken(),array('id'=>false));
if($hiddens!==array())
$form.="\n".self::tag('div',array('style'=>'display:none'),implode("\n",$hiddens));
@@ -4084,7 +4236,10 @@ public static function radioButton($name,$checked=false,$htmlOptions=array())
$uncheck=null;
if(!isset($htmlOptions['id']))
$htmlOptions['id']=self::getIdByName($name);
- $hidden=$uncheck!==null ? self::hiddenField($name,$uncheck,array('id'=>self::ID_PREFIX.$htmlOptions['id'])) : '';
+ else if($htmlOptions['id']===false)
+ unset($htmlOptions['id']);
+ $uncheckOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array();
+ $hidden=$uncheck!==null ? self::hiddenField($name,$uncheck,$uncheckOptions) : '';
// add a hidden field so that if the radio button is not selected, it still submits a value
return $hidden . self::inputField('radio',$name,$value,$htmlOptions);
}
@@ -4105,7 +4260,10 @@ public static function checkBox($name,$checked=false,$htmlOptions=array())
$uncheck=null;
if(!isset($htmlOptions['id']))
$htmlOptions['id']=self::getIdByName($name);
- $hidden=$uncheck!==null ? self::hiddenField($name,$uncheck,array('id'=>self::ID_PREFIX.$htmlOptions['id'])) : '';
+ else if($htmlOptions['id']===false)
+ unset($htmlOptions['id']);
+ $uncheckOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array();
+ $hidden=$uncheck!==null ? self::hiddenField($name,$uncheck,$uncheckOptions) : '';
// add a hidden field so that if the checkbox is not selected, it still submits a value
return $hidden . self::inputField('checkbox',$name,$value,$htmlOptions);
}
@@ -4340,14 +4498,16 @@ public static function activeTextArea($model,$attribute,$htmlOptions=array())
self::clientChange('change',$htmlOptions);
if($model->hasErrors($attribute))
self::addErrorCss($htmlOptions);
- return self::tag('textarea',$htmlOptions,isset($htmlOptions['encode']) && !$htmlOptions['encode'] ? $model->$attribute : self::encode($model->$attribute));
+ $text=self::resolveValue($model,$attribute);
+ return self::tag('textarea',$htmlOptions,isset($htmlOptions['encode']) && !$htmlOptions['encode'] ? $text : self::encode($text));
}
public static function activeFileField($model,$attribute,$htmlOptions=array())
{
self::resolveNameID($model,$attribute,$htmlOptions);
// add a hidden field so that if a model only has a file field, we can
// still use isset($_POST[$modelClass]) to detect if the input is submitted
- return self::hiddenField($htmlOptions['name'],'',array('id'=>self::ID_PREFIX.$htmlOptions['id']))
+ $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array();
+ return self::hiddenField($htmlOptions['name'],'',$hiddenOptions)
. self::activeInputField('file',$model,$attribute,$htmlOptions);
}
public static function activeRadioButton($model,$attribute,$htmlOptions=array())
@@ -4355,7 +4515,7 @@ public static function activeRadioButton($model,$attribute,$htmlOptions=array())
self::resolveNameID($model,$attribute,$htmlOptions);
if(!isset($htmlOptions['value']))
$htmlOptions['value']=1;
- if(!isset($htmlOptions['checked']) && $model->$attribute==$htmlOptions['value'])
+ if(!isset($htmlOptions['checked']) && self::resolveValue($model,$attribute)==$htmlOptions['value'])
$htmlOptions['checked']='checked';
self::clientChange('click',$htmlOptions);
if(array_key_exists('uncheckValue',$htmlOptions))
@@ -4365,7 +4525,8 @@ public static function activeRadioButton($model,$attribute,$htmlOptions=array())
}
else
$uncheck='0';
- $hidden=$uncheck!==null ? self::hiddenField($htmlOptions['name'],$uncheck,array('id'=>self::ID_PREFIX.$htmlOptions['id'])) : '';
+ $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array();
+ $hidden=$uncheck!==null ? self::hiddenField($htmlOptions['name'],$uncheck,$hiddenOptions) : '';
// add a hidden field so that if the radio button is not selected, it still submits a value
return $hidden . self::activeInputField('radio',$model,$attribute,$htmlOptions);
}
@@ -4374,7 +4535,7 @@ public static function activeCheckBox($model,$attribute,$htmlOptions=array())
self::resolveNameID($model,$attribute,$htmlOptions);
if(!isset($htmlOptions['value']))
$htmlOptions['value']=1;
- if(!isset($htmlOptions['checked']) && $model->$attribute==$htmlOptions['value'])
+ if(!isset($htmlOptions['checked']) && self::resolveValue($model,$attribute)==$htmlOptions['value'])
$htmlOptions['checked']='checked';
self::clientChange('click',$htmlOptions);
if(array_key_exists('uncheckValue',$htmlOptions))
@@ -4384,13 +4545,14 @@ public static function activeCheckBox($model,$attribute,$htmlOptions=array())
}
else
$uncheck='0';
- $hidden=$uncheck!==null ? self::hiddenField($htmlOptions['name'],$uncheck,array('id'=>self::ID_PREFIX.$htmlOptions['id'])) : '';
+ $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array();
+ $hidden=$uncheck!==null ? self::hiddenField($htmlOptions['name'],$uncheck,$hiddenOptions) : '';
return $hidden . self::activeInputField('checkbox',$model,$attribute,$htmlOptions);
}
public static function activeDropDownList($model,$attribute,$data,$htmlOptions=array())
{
self::resolveNameID($model,$attribute,$htmlOptions);
- $selection=$model->$attribute;
+ $selection=self::resolveValue($model,$attribute);
$options="\n".self::listOptions($selection,$data,$htmlOptions);
self::clientChange('change',$htmlOptions);
if($model->hasErrors($attribute))
@@ -4411,23 +4573,25 @@ public static function activeListBox($model,$attribute,$data,$htmlOptions=array(
public static function activeCheckBoxList($model,$attribute,$data,$htmlOptions=array())
{
self::resolveNameID($model,$attribute,$htmlOptions);
- $selection=$model->$attribute;
+ $selection=self::resolveValue($model,$attribute);
if($model->hasErrors($attribute))
self::addErrorCss($htmlOptions);
$name=$htmlOptions['name'];
unset($htmlOptions['name']);
- return self::hiddenField($name,'',array('id'=>self::ID_PREFIX.$htmlOptions['id']))
+ $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array();
+ return self::hiddenField($name,'',$hiddenOptions)
. self::checkBoxList($name,$selection,$data,$htmlOptions);
}
public static function activeRadioButtonList($model,$attribute,$data,$htmlOptions=array())
{
self::resolveNameID($model,$attribute,$htmlOptions);
- $selection=$model->$attribute;
+ $selection=self::resolveValue($model,$attribute);
if($model->hasErrors($attribute))
self::addErrorCss($htmlOptions);
$name=$htmlOptions['name'];
unset($htmlOptions['name']);
- return self::hiddenField($name,'',array('id'=>self::ID_PREFIX.$htmlOptions['id']))
+ $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array();
+ return self::hiddenField($name,'',$hiddenOptions)
. self::radioButtonList($name,$selection,$data,$htmlOptions);
}
public static function getActiveId($model,$attribute)
@@ -4439,6 +4603,13 @@ public static function errorSummary($model,$header=null,$footer=null,$htmlOption
$content='';
if(!is_array($model))
$model=array($model);
+ if(isset($htmlOptions['firstError']))
+ {
+ $firstError=$htmlOptions['firstError'];
+ unset($htmlOptions['firstError']);
+ }
+ else
+ $firstError=false;
foreach($model as $m)
{
foreach($m->getErrors() as $errors)
@@ -4447,6 +4618,8 @@ public static function errorSummary($model,$header=null,$footer=null,$htmlOption
{
if($error!='')
$content.="
$error\n";
+ if($firstError)
+ break;
}
}
}
@@ -4526,10 +4699,26 @@ public static function activeName($model,$attribute)
protected static function activeInputField($type,$model,$attribute,$htmlOptions)
{
$htmlOptions['type']=$type;
+ if($type==='text' || $type==='password')
+ {
+ if(!isset($htmlOptions['maxlength']))
+ {
+ foreach($model->getValidators($attribute) as $validator)
+ {
+ if($validator instanceof CStringValidator && $validator->max!==null)
+ {
+ $htmlOptions['maxlength']=$validator->max;
+ break;
+ }
+ }
+ }
+ else if($htmlOptions['maxlength']===false)
+ unset($htmlOptions['maxlength']);
+ }
if($type==='file')
unset($htmlOptions['value']);
else if(!isset($htmlOptions['value']))
- $htmlOptions['value']=$model->$attribute;
+ $htmlOptions['value']=self::resolveValue($model,$attribute);
if($model->hasErrors($attribute))
self::addErrorCss($htmlOptions);
return self::tag('input',$htmlOptions);
@@ -4563,6 +4752,17 @@ public static function listOptions($selection,$listData,&$htmlOptions)
}
else
$options=array();
+ $key=isset($htmlOptions['key']) ? $htmlOptions['key'] : 'primaryKey';
+ if(is_array($selection))
+ {
+ foreach($selection as $i=>$item)
+ {
+ if(is_object($item))
+ $selection[$i]=$item->$key;
+ }
+ }
+ else if(is_object($selection))
+ $selection=$selection->$key;
foreach($listData as $key=>$value)
{
if(is_array($value))
@@ -4584,6 +4784,7 @@ public static function listOptions($selection,$listData,&$htmlOptions)
$content.=self::tag('option',$attributes,$raw?(string)$value : self::encode((string)$value))."\n";
}
}
+ unset($htmlOptions['key']);
return $content;
}
protected static function clientChange($event,&$htmlOptions,$live=true)
@@ -4641,9 +4842,8 @@ protected static function clientChange($event,&$htmlOptions,$live=true)
}
public static function resolveNameID($model,&$attribute,&$htmlOptions)
{
- $name=self::resolveName($model,$attribute);
if(!isset($htmlOptions['name']))
- $htmlOptions['name']=$name;
+ $htmlOptions['name']=self::resolveName($model,$attribute);
if(!isset($htmlOptions['id']))
$htmlOptions['id']=self::getIdByName($htmlOptions['name']);
else if($htmlOptions['id']===false)
@@ -4651,11 +4851,43 @@ public static function resolveNameID($model,&$attribute,&$htmlOptions)
}
public static function resolveName($model,&$attribute)
{
- if('['===$attribute[0])
- list($i, $attribute, $index)=array(strtok($attribute, '[]'), strtok('['), strtok(']'));
+ if(($pos=strpos($attribute,'['))!==false)
+ {
+ if($pos!==0) // e.g. name[a][b]
+ return get_class($model).'['.substr($attribute,0,$pos).']'.substr($attribute,$pos);
+ if(($pos=strrpos($attribute,']'))!==false && $pos!==strlen($attribute)-1) // e.g. [a][b]name
+ {
+ $sub=substr($attribute,0,$pos+1);
+ $attribute=substr($attribute,$pos+1);
+ return get_class($model).$sub.'['.$attribute.']';
+ }
+ if(preg_match('/\](\w+\[.*)$/',$attribute,$matches))
+ {
+ $name=get_class($model).'['.str_replace(']','][',trim(strtr($attribute,array(']['=>']','['=>']')),']')).']';
+ $attribute=$matches[1];
+ return $name;
+ }
+ }
+ else
+ return get_class($model).'['.$attribute.']';
+ }
+ public static function resolveValue($model,$attribute)
+ {
+ if(($pos=strpos($attribute,'['))!==false)
+ {
+ $name=substr($attribute,0,$pos);
+ $value=$model->$name;
+ foreach(explode('][',rtrim(substr($attribute,$pos+1),']')) as $id)
+ {
+ if(is_array($value) && isset($value[$id]))
+ $value=$value[$id];
+ else
+ return null;
+ }
+ return $value;
+ }
else
- list($attribute, $index)=array(strtok($attribute, '['), strtok(']'));
- return get_class($model).(isset($i) ? '['.$i.']' : '').'['.$attribute.']'.(false!==$index ? '['.$index.']' : '');
+ return $model->$attribute;
}
protected static function addErrorCss(&$htmlOptions)
{
@@ -4684,6 +4916,63 @@ protected static function renderAttributes($htmlOptions)
return $html;
}
}
+class CWidgetFactory extends CApplicationComponent implements IWidgetFactory
+{
+ public $enableSkin=false;
+ public $widgets=array();
+ public $skinnableWidgets;
+ public $skinPath;
+ private $_skins=array(); // class name, skin name, property name => value
+ public function init()
+ {
+ parent::init();
+ if($this->enableSkin && $this->skinPath===null)
+ $this->skinPath=Yii::app()->getViewPath().DIRECTORY_SEPARATOR.'skins';
+ }
+ public function createWidget($owner,$className,$properties=array())
+ {
+ $className=Yii::import($className,true);
+ $widget=new $className($owner);
+ if(isset($this->widgets[$className]))
+ $properties=$properties===array() ? $this->widgets[$className] : CMap::mergeArray($this->widgets[$className],$properties);
+ if($this->enableSkin)
+ {
+ if($this->skinnableWidgets===null || in_array($className,$this->skinnableWidgets))
+ {
+ $skinName=isset($properties['skin']) ? $properties['skin'] : 'default';
+ if($skinName!==false && ($skin=$this->getSkin($className,$skinName))!==array())
+ $properties=$properties===array() ? $skin : CMap::mergeArray($skin,$properties);
+ }
+ }
+ foreach($properties as $name=>$value)
+ $widget->$name=$value;
+ return $widget;
+ }
+ protected function getSkin($className,$skinName)
+ {
+ if(!isset($this->_skins[$className][$skinName]))
+ {
+ $skinFile=$this->skinPath.DIRECTORY_SEPARATOR.$className.'.php';
+ if(is_file($skinFile))
+ $this->_skins[$className]=require($skinFile);
+ else
+ $this->_skins[$className]=array();
+ if(($theme=Yii::app()->getTheme())!==null)
+ {
+ $skinFile=$theme->getSkinPath().DIRECTORY_SEPARATOR.$className.'.php';
+ if(is_file($skinFile))
+ {
+ $skins=require($skinFile);
+ foreach($skins as $name=>$skin)
+ $this->_skins[$className][$name]=$skin;
+ }
+ }
+ if(!isset($this->_skins[$className][$skinName]))
+ $this->_skins[$className][$skinName]=array();
+ }
+ return $this->_skins[$className][$skinName];
+ }
+}
class CWidget extends CBaseController
{
public $actionPrefix;
@@ -4777,24 +5066,25 @@ class CClientScript extends CApplicationComponent
protected $cssFiles=array();
protected $scriptFiles=array();
protected $scripts=array();
+ protected $metaTags=array();
+ protected $linkTags=array();
+ protected $css=array();
+ public $coreScriptPosition=self::POS_HEAD;
private $_hasScripts=false;
private $_packages;
private $_dependencies;
private $_baseUrl;
private $_coreScripts=array();
- private $_css=array();
- private $_metas=array();
- private $_links=array();
public function reset()
{
$this->_hasScripts=false;
$this->_coreScripts=array();
$this->cssFiles=array();
- $this->_css=array();
+ $this->css=array();
$this->scriptFiles=array();
$this->scripts=array();
- $this->_metas=array();
- $this->_links=array();
+ $this->metaTags=array();
+ $this->linkTags=array();
$this->recordCachingAction('clientScript','reset',array());
}
public function render(&$output)
@@ -4881,24 +5171,24 @@ public function renderCoreScripts()
}
if($jsFiles!==array())
{
- if(isset($this->scriptFiles[self::POS_HEAD]))
+ if(isset($this->scriptFiles[$this->coreScriptPosition]))
{
- foreach($this->scriptFiles[self::POS_HEAD] as $url)
+ foreach($this->scriptFiles[$this->coreScriptPosition] as $url)
$jsFiles[$url]=$url;
}
- $this->scriptFiles[self::POS_HEAD]=$jsFiles;
+ $this->scriptFiles[$this->coreScriptPosition]=$jsFiles;
}
}
public function renderHead(&$output)
{
$html='';
- foreach($this->_metas as $meta)
+ foreach($this->metaTags as $meta)
$html.=CHtml::metaTag($meta['content'],null,null,$meta)."\n";
- foreach($this->_links as $link)
+ foreach($this->linkTags as $link)
$html.=CHtml::linkTag(null,null,null,null,$link)."\n";
foreach($this->cssFiles as $url=>$media)
$html.=CHtml::cssFile($url,$media)."\n";
- foreach($this->_css as $css)
+ foreach($this->css as $css)
$html.=CHtml::css($css[0],$css[1])."\n";
if($this->enableJavaScript)
{
@@ -5018,7 +5308,7 @@ public function registerCssFile($url,$media='')
public function registerCss($id,$css,$media='')
{
$this->_hasScripts=true;
- $this->_css[$id]=array($css,$media);
+ $this->css[$id]=array($css,$media);
$params=func_get_args();
$this->recordCachingAction('clientScript','registerCss',$params);
}
@@ -5046,7 +5336,7 @@ public function registerMetaTag($content,$name=null,$httpEquiv=null,$options=arr
if($httpEquiv!==null)
$options['http-equiv']=$httpEquiv;
$options['content']=$content;
- $this->_metas[serialize($options)]=$options;
+ $this->metaTags[serialize($options)]=$options;
$params=func_get_args();
$this->recordCachingAction('clientScript','registerMetaTag',$params);
}
@@ -5061,7 +5351,7 @@ public function registerLinkTag($relation=null,$type=null,$href=null,$media=null
$options['href']=$href;
if($media!==null)
$options['media']=$media;
- $this->_links[serialize($options)]=$options;
+ $this->linkTags[serialize($options)]=$options;
$params=func_get_args();
$this->recordCachingAction('clientScript','registerLinkTag',$params);
}
@@ -5071,7 +5361,7 @@ public function isCssFileRegistered($url)
}
public function isCssRegistered($id)
{
- return isset($this->_css[$id]);
+ return isset($this->css[$id]);
}
public function isScriptFileRegistered($url,$position=self::POS_HEAD)
{
@@ -5685,6 +5975,13 @@ public function setAttributes($values,$safeOnly=true)
$this->onUnsafeAttribute($name,$value);
}
}
+ public function unsetAttributes($names=null)
+ {
+ if($names===null)
+ $names=$this->attributeNames();
+ foreach($names as $name)
+ $this->$name=null;
+ }
public function onUnsafeAttribute($name,$value)
{
if(YII_DEBUG)
@@ -5756,6 +6053,7 @@ abstract class CActiveRecord extends CModel
private $_related=array(); // attribute name => related objects
private $_c; // query criteria (used by finder only)
private $_pk; // old primary key value
+ private $_alias='t'; // the table alias being used for query
public function __construct($scenario='insert')
{
if($scenario===null) // internally used by populateRecord() and model()
@@ -5853,11 +6151,11 @@ public function getRelated($name,$refresh=false,$params=array())
$exists=isset($this->_related[$name]) || array_key_exists($name,$this->_related);
if($exists)
$save=$this->_related[$name];
- unset($this->_related[$name]);
$r=array($name=>$params);
}
else
$r=$name;
+ unset($this->_related[$name]);
$finder=new CActiveFinder($this,$r);
$finder->lazyFind($this);
if(!isset($this->_related[$name]))
@@ -5894,14 +6192,17 @@ public function getDbCriteria($createIfNull=true)
}
return $this->_c;
}
+ public function setDbCriteria($criteria)
+ {
+ $this->_c=$criteria;
+ }
public function defaultScope()
{
return array();
}
public function resetScope()
{
- if($this->_c!==null)
- $this->_c=new CDbCriteria();
+ $this->_c=new CDbCriteria();
return $this;
}
public static function model($className=__CLASS__)
@@ -6285,6 +6586,10 @@ public function getOldPrimaryKey()
{
return $this->_pk;
}
+ public function setOldPrimaryKey($value)
+ {
+ $this->_pk=$value;
+ }
private function query($criteria,$all=false)
{
$this->applyScopes($criteria);
@@ -6308,14 +6613,18 @@ public function applyScopes(&$criteria)
$this->_c=null;
}
}
- public function getTableAlias($quote=false)
+ public function getTableAlias($quote=false, $checkScopes=true)
{
- if(($criteria=$this->getDbCriteria(false))!==null && $criteria->alias!='')
+ if($checkScopes && ($criteria=$this->getDbCriteria(false))!==null && $criteria->alias!='')
$alias=$criteria->alias;
else
- $alias='t';
+ $alias=$this->_alias;
return $quote ? $this->getDbConnection()->getSchema()->quoteTableName($alias) : $alias;
}
+ public function setTableAlias($alias)
+ {
+ $this->_alias=$alias;
+ }
public function find($condition='',$params=array())
{
$criteria=$this->getCommandBuilder()->createCriteria($condition,$params);
@@ -6352,11 +6661,13 @@ public function findAllByAttributes($attributes,$condition='',$params=array())
}
public function findBySql($sql,$params=array())
{
+ $this->beforeFind();
$command=$this->getCommandBuilder()->createSqlCommand($sql,$params);
return $this->populateRecord($command->queryRow());
}
public function findAllBySql($sql,$params=array())
{
+ $this->beforeFind();
$command=$this->getCommandBuilder()->createSqlCommand($sql,$params);
return $this->populateRecords($command->queryAll());
}
@@ -6468,7 +6779,11 @@ public function populateRecords($data,$callAfterFind=true)
{
$records=array();
foreach($data as $attributes)
- $records[]=$this->populateRecord($attributes,$callAfterFind);
+ {
+ $record=$this->populateRecord($attributes,$callAfterFind);
+ if($record!==null)
+ $records[]=$record;
+ }
return $records;
}
protected function instantiate($attributes)
@@ -6491,6 +6806,7 @@ class CBaseActiveRelation extends CComponent
public $condition='';
public $params=array();
public $group='';
+ public $join='';
public $having='';
public $order='';
public function __construct($name,$className,$foreignKey,$options=array())
@@ -6501,7 +6817,7 @@ public function __construct($name,$className,$foreignKey,$options=array())
foreach($options as $name=>$value)
$this->$name=$value;
}
- public function mergeWith($criteria)
+ public function mergeWith($criteria,$fromScope=false)
{
if(isset($criteria['select']) && $this->select!==$criteria['select'])
{
@@ -6537,6 +6853,13 @@ public function mergeWith($criteria)
else if($criteria['group']!=='')
$this->group.=', '.$criteria['group'];
}
+ if(isset($criteria['join']) && $this->join!==$criteria['join'])
+ {
+ if($this->join==='')
+ $this->join=$criteria['join'];
+ else if($criteria['join']!=='')
+ $this->join.=' '.$criteria['join'];
+ }
if(isset($criteria['having']) && $this->having!==$criteria['having'])
{
if($this->having==='')
@@ -6550,9 +6873,9 @@ class CStatRelation extends CBaseActiveRelation
{
public $select='COUNT(*)';
public $defaultValue=0;
- public function mergeWith($criteria)
+ public function mergeWith($criteria,$fromScope=false)
{
- parent::mergeWith($criteria);
+ parent::mergeWith($criteria,$fromScope);
if(isset($criteria['defaultValue']))
$this->defaultValue=$criteria['defaultValue'];
}
@@ -6563,8 +6886,19 @@ class CActiveRelation extends CBaseActiveRelation
public $on='';
public $alias;
public $with=array();
- public function mergeWith($criteria)
+ public function mergeWith($criteria,$fromScope=false)
{
+ if($fromScope)
+ {
+ if(isset($criteria['condition']) && $this->on!==$criteria['condition'])
+ {
+ if($this->on==='')
+ $this->on=$criteria['condition'];
+ else if($criteria['condition']!=='')
+ $this->on="({$this->on}) AND ({$criteria['condition']})";
+ }
+ unset($criteria['condition']);
+ }
parent::mergeWith($criteria);
if(isset($criteria['joinType']))
$this->joinType=$criteria['joinType'];
@@ -6595,9 +6929,9 @@ class CHasManyRelation extends CActiveRelation
public $offset=-1;
public $together=true;
public $index;
- public function mergeWith($criteria)
+ public function mergeWith($criteria,$fromScope=false)
{
- parent::mergeWith($criteria);
+ parent::mergeWith($criteria,$fromScope);
if(isset($criteria['limit']) && $criteria['limit']>0)
$this->limit=$criteria['limit'];
if(isset($criteria['offset']) && $criteria['offset']>=0)
@@ -6722,8 +7056,16 @@ protected function open()
}
catch(PDOException $e)
{
- throw new CDbException(Yii::t('yii','CDbConnection failed to open the DB connection: {error}',
- array('{error}'=>$e->getMessage())));
+ if(YII_DEBUG)
+ {
+ throw new CDbException(Yii::t('yii','CDbConnection failed to open the DB connection: {error}',
+ array('{error}'=>$e->getMessage())));
+ }
+ else
+ {
+ Yii::log($e->getMessage(),CLogger::LEVEL_ERROR,'exception.CDbException');
+ throw new CDbException(Yii::t('yii','CDbConnection failed to open the DB connection.'));
+ }
}
}
}
@@ -6968,18 +7310,26 @@ public function getTable($name)
{
if(isset($this->_tables[$name]))
return $this->_tables[$name];
- else if(!isset($this->_cacheExclude[$name]) && ($duration=$this->_connection->schemaCachingDuration)>0 && $this->_connection->schemaCacheID!==false && ($cache=Yii::app()->getComponent($this->_connection->schemaCacheID))!==null)
+ else
{
- $key='yii:dbschema'.$this->_connection->connectionString.':'.$this->_connection->username.':'.$name;
- if(($table=$cache->get($key))===false)
+ if($this->_connection->tablePrefix!='' && strpos($name,'{{')!==false)
+ $realName=preg_replace('/\{\{(.*?)\}\}/',$this->_connection->tablePrefix.'$1',$name);
+ else
+ $realName=$name;
+ if(!isset($this->_cacheExclude[$name]) && ($duration=$this->_connection->schemaCachingDuration)>0 && $this->_connection->schemaCacheID!==false && ($cache=Yii::app()->getComponent($this->_connection->schemaCacheID))!==null)
{
- $table=$this->createTable($name);
- $cache->set($key,$table,$duration);
+ $key='yii:dbschema'.$this->_connection->connectionString.':'.$this->_connection->username.':'.$name;
+ if(($table=$cache->get($key))===false)
+ {
+ $table=$this->createTable($realName);
+ if($table!==null)
+ $cache->set($key,$table,$duration);
+ }
+ return $this->_tables[$name]=$table;
}
- return $this->_tables[$name]=$table;
+ else
+ return $this->_tables[$name]=$this->createTable($realName);
}
- else
- return $this->_tables[$name]=$this->createTable($name);
}
public function getTables($schema='')
{
@@ -7235,7 +7585,7 @@ public function bindParam($name, &$value, $dataType=null, $length=null)
else
$this->_statement->bindParam($name,$value,$dataType,$length);
if($this->_connection->enableParamLogging)
- $this->_params[$name]='['.gettype($value).']';
+ $this->_params[$name]=&$value;
return $this;
}
public function bindValue($name, $value, $dataType=null)
@@ -7253,9 +7603,10 @@ public function execute($params=array())
{
if($this->_connection->enableParamLogging && ($pars=array_merge($this->_params,$params))!==array())
{
+ $p=array();
foreach($pars as $name=>$value)
- $pars[$name]=$name.'='.$value;
- $par='. Bind with parameter ' .implode(', ',$pars);
+ $p[$name]=$name.'='.$value;
+ $par='. Bind with parameter ' .implode(', ',$p);
}
else
$par='';
@@ -7307,9 +7658,10 @@ private function queryInternal($method,$mode,$params=array())
{
if($this->_connection->enableParamLogging && ($pars=array_merge($this->_params,$params))!==array())
{
+ $p=array();
foreach($pars as $name=>$value)
- $pars[$name]=$name.'='.$value;
- $par='. Bind with parameter ' .implode(', ',$pars);
+ $p[$name]=$name.'='.$value;
+ $par='. Bind with parameter ' .implode(', ',$p);
}
else
$par='';
@@ -7363,7 +7715,7 @@ public function init($dbType, $defaultValue)
}
protected function extractType($dbType)
{
- if(stripos($dbType,'int')!==false)
+ if(stripos($dbType,'int')!==false && stripos($dbType,'unsigned int')===false)
$this->type='integer';
else if(stripos($dbType,'bool')!==false)
$this->type='boolean';
@@ -7412,6 +7764,226 @@ protected function extractDefault($defaultValue)
$this->defaultValue=$this->typecast($defaultValue);
}
}
+abstract class CValidator extends CComponent
+{
+ public static $builtInValidators=array(
+ 'required'=>'CRequiredValidator',
+ 'filter'=>'CFilterValidator',
+ 'match'=>'CRegularExpressionValidator',
+ 'email'=>'CEmailValidator',
+ 'url'=>'CUrlValidator',
+ 'unique'=>'CUniqueValidator',
+ 'compare'=>'CCompareValidator',
+ 'length'=>'CStringValidator',
+ 'in'=>'CRangeValidator',
+ 'numerical'=>'CNumberValidator',
+ 'captcha'=>'CCaptchaValidator',
+ 'type'=>'CTypeValidator',
+ 'file'=>'CFileValidator',
+ 'default'=>'CDefaultValueValidator',
+ 'exist'=>'CExistValidator',
+ 'boolean'=>'CBooleanValidator',
+ 'safe'=>'CSafeValidator',
+ 'unsafe'=>'CUnsafeValidator',
+ );
+ public $attributes;
+ public $message;
+ public $skipOnError=false;
+ public $on;
+ abstract protected function validateAttribute($object,$attribute);
+ public static function createValidator($name,$object,$attributes,$params)
+ {
+ if(is_string($attributes))
+ $attributes=preg_split('/[\s,]+/',$attributes,-1,PREG_SPLIT_NO_EMPTY);
+ if(isset($params['on']))
+ {
+ if(is_array($params['on']))
+ $on=$params['on'];
+ else
+ $on=preg_split('/[\s,]+/',$params['on'],-1,PREG_SPLIT_NO_EMPTY);
+ }
+ else
+ $on=array();
+ if(method_exists($object,$name))
+ {
+ $validator=new CInlineValidator;
+ $validator->attributes=$attributes;
+ $validator->method=$name;
+ $validator->params=$params;
+ if(isset($params['skipOnError']))
+ $validator->skipOnError=$params['skipOnError'];
+ }
+ else
+ {
+ $params['attributes']=$attributes;
+ if(isset(self::$builtInValidators[$name]))
+ $className=Yii::import(self::$builtInValidators[$name],true);
+ else
+ $className=Yii::import($name,true);
+ $validator=new $className;
+ foreach($params as $name=>$value)
+ $validator->$name=$value;
+ }
+ $validator->on=empty($on) ? array() : array_combine($on,$on);
+ return $validator;
+ }
+ public function validate($object,$attributes=null)
+ {
+ if(is_array($attributes))
+ $attributes=array_intersect($this->attributes,$attributes);
+ else
+ $attributes=$this->attributes;
+ foreach($attributes as $attribute)
+ {
+ if(!$this->skipOnError || !$object->hasErrors($attribute))
+ $this->validateAttribute($object,$attribute);
+ }
+ }
+ public function applyTo($scenario)
+ {
+ return empty($this->on) || isset($this->on[$scenario]);
+ }
+ protected function addError($object,$attribute,$message,$params=array())
+ {
+ $params['{attribute}']=$object->getAttributeLabel($attribute);
+ $object->addError($attribute,strtr($message,$params));
+ }
+ protected function isEmpty($value,$trim=false)
+ {
+ return $value===null || $value===array() || $value==='' || $trim && is_scalar($value) && trim($value)==='';
+ }
+}
+class CStringValidator extends CValidator
+{
+ public $max;
+ public $min;
+ public $is;
+ public $tooShort;
+ public $tooLong;
+ public $allowEmpty=true;
+ public $encoding=false;
+ protected function validateAttribute($object,$attribute)
+ {
+ $value=$object->$attribute;
+ if($this->allowEmpty && $this->isEmpty($value))
+ return;
+ if($this->encoding!==false && function_exists('mb_strlen'))
+ $length=mb_strlen($value,$this->encoding);
+ else
+ $length=strlen($value);
+ if($this->min!==null && $length<$this->min)
+ {
+ $message=$this->tooShort!==null?$this->tooShort:Yii::t('yii','{attribute} is too short (minimum is {min} characters).');
+ $this->addError($object,$attribute,$message,array('{min}'=>$this->min));
+ }
+ if($this->max!==null && $length>$this->max)
+ {
+ $message=$this->tooLong!==null?$this->tooLong:Yii::t('yii','{attribute} is too long (maximum is {max} characters).');
+ $this->addError($object,$attribute,$message,array('{max}'=>$this->max));
+ }
+ if($this->is!==null && $length!==$this->is)
+ {
+ $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} is of the wrong length (should be {length} characters).');
+ $this->addError($object,$attribute,$message,array('{length}'=>$this->is));
+ }
+ }
+}
+class CRequiredValidator extends CValidator
+{
+ public $requiredValue;
+ public $strict=false;
+ protected function validateAttribute($object,$attribute)
+ {
+ $value=$object->$attribute;
+ if($this->requiredValue!==null)
+ {
+ if(!$this->strict && $value!=$this->requiredValue || $this->strict && $value!==$this->requiredValue)
+ {
+ $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} must be {value}.',
+ array('{value}'=>$this->requiredValue));
+ $this->addError($object,$attribute,$message);
+ }
+ }
+ else if($this->isEmpty($value,true))
+ {
+ $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} cannot be blank.');
+ $this->addError($object,$attribute,$message);
+ }
+ }
+}
+class CNumberValidator extends CValidator
+{
+ public $integerOnly=false;
+ public $allowEmpty=true;
+ public $max;
+ public $min;
+ public $tooBig;
+ public $tooSmall;
+ protected function validateAttribute($object,$attribute)
+ {
+ $value=$object->$attribute;
+ if($this->allowEmpty && $this->isEmpty($value))
+ return;
+ if($this->integerOnly)
+ {
+ if(!preg_match('/^\s*[+-]?\d+\s*$/',"$value"))
+ {
+ $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} must be an integer.');
+ $this->addError($object,$attribute,$message);
+ }
+ }
+ else
+ {
+ if(!preg_match('/^\s*[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\s*$/',"$value"))
+ {
+ $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} must be a number.');
+ $this->addError($object,$attribute,$message);
+ }
+ }
+ if($this->min!==null && $value<$this->min)
+ {
+ $message=$this->tooSmall!==null?$this->tooSmall:Yii::t('yii','{attribute} is too small (minimum is {min}).');
+ $this->addError($object,$attribute,$message,array('{min}'=>$this->min));
+ }
+ if($this->max!==null && $value>$this->max)
+ {
+ $message=$this->tooBig!==null?$this->tooBig:Yii::t('yii','{attribute} is too big (maximum is {max}).');
+ $this->addError($object,$attribute,$message,array('{max}'=>$this->max));
+ }
+ }
+}
+class CListIterator 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;
+ }
+}
interface IApplicationComponent
{
public function init();