From 8ebf68691465bea15379155fc835627c3812fbdf Mon Sep 17 00:00:00 2001 From: "qiang.xue" Date: Thu, 15 Jan 2009 12:55:57 +0000 Subject: [PATCH] merge from 1.0. --- CHANGELOG | 3 +- UPGRADE | 58 +--- demos/blog/protected/models/LoginForm.php | 2 - demos/blog/protected/models/Post.php | 12 +- demos/phonebook/protected/models/Contact.php | 7 - docs/guide/form.model.txt | 108 +++--- framework/YiiBase.php | 2 +- framework/base/CModel.php | 327 ++++++++++++------ .../cli/views/shell/controller/controller.php | 11 - framework/db/ar/CActiveRecord.php | 160 ++++----- framework/messages/config.php | 2 +- framework/messages/ja/yii.php | 208 +++++++++++ framework/validators/CSafeValidator.php | 32 -- framework/validators/CValidator.php | 40 ++- framework/web/CController.php | 2 +- framework/web/CFormModel.php | 65 ++-- framework/web/CWebApplication.php | 2 +- framework/web/auth/CAccessControlFilter.php | 32 +- framework/web/auth/CWebUser.php | 11 +- framework/web/helpers/CHtml.php | 11 +- framework/yiilite.php | 236 +++++++------ requirements/messages/config.php | 2 +- requirements/messages/ja/yii.php | 44 +++ tests/ut/framework/db/data/models.php | 7 - tests/ut/framework/db/data/models2.php | 7 - 25 files changed, 843 insertions(+), 548 deletions(-) create mode 100644 framework/messages/ja/yii.php delete mode 100644 framework/validators/CSafeValidator.php create mode 100644 requirements/messages/ja/yii.php diff --git a/CHANGELOG b/CHANGELOG index 50f2f7d396..ce3df05ae7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,7 @@ Version 1.0.2 to be released - Bug: CDbAuthManager::saveAuthAssignment() causes updating all rows (Qiang) - Bug: Fixed an issue in CUrlManager::createUrl when GET parameters contain arrays (Qiang) - New #88: Added public properties to CActiveRecord::safeAttributes() (Qiang) +- New #92: Empty error messages in models are handled better when being displayed (Qiang) - New: Added CActiveRecord::getRelated() (Qiang) - New: Added 'return' option to HTML options in CHtml (Qiang) - New: Refactored CSS-dependent widgets by adding registerCssFile static methods (Qiang) @@ -23,7 +24,7 @@ Version 1.0.2 to be released - New: Added scenario-based massive model attribute assignment (Qiang) - New: Added support to specify views in terms of path aliases (Qiang) - New: Enhanced getBaseUrl to allow it to return an absolute URL (Qiang) - +- New: Allow flash message to be deleted right after its first access (Qiang) Version 1.0.1 January 4, 2009 ----------------------------- diff --git a/UPGRADE b/UPGRADE index e84b7f3947..ffcb944d0a 100644 --- a/UPGRADE +++ b/UPGRADE @@ -12,66 +12,14 @@ for both A and B. Upgrading from v1.0.1 --------------------- -- CActiveRecord::saveAttributes() is removed. Please use the following alternative: - - $model->setAttributes($values,false); - $model->update(); - - - Due to the introduction of the scenario-based massive assignment feature, - we have to make the following BC breaking changes. Please be very careful - if you want to upgrade, as the changes will most likely affect or break - your existing code. - - The signature of the following methods in CModel (inheirted by CFormModel - and CActiveRecord) is changed: - - public function validate($scenario='',$attributes=null) - public function setAttributes($values,$scenario='') - - And the following methods are removed from CActiveRecord: - - public function protectedAttributes() - public function safeAttributes() - - - The above changes will affect your code if you call any of the above - methods or if your code contains the following assignment statements: - - $model->attributes=$data; - - - Starting from version 1.0.2, massive attribute assignment is based on - validation rules and scenarios. In particular, an attribute can be - massively assigned if it meets both of the following conditions: - - * The attribute name appears in the attribute list of some validation rule - which applies to the current scenario. A validation rule applies to - a specified scenario if the `on` option of the rule is not set or - contains the specified scenario name. - - * The attribute name is a valid model attribute. For CFormModel, - this means the entry name must refer to a public member variable of - the model class; while for CActiveRecord, this means the entry name - must refer to either a column of the associated DB table or a member - variable of the class. - - As a result, if you previously used either protectedAttributes() or - safeAttributes() to limit massive assignments, you should change to - use validation rules to specify this limit. For attributes that don't - need validation but still need massive assignments, they can be listed - in a 'safe' validation rule. + we removed CActiveRecord::protectedAttributes(). Please use safeAttributes() + to specify which attributes are safe to be massively assigned. For more details about the scenario-based assignment and validation, - please read the following tutorial sections: + please read the following tutorial section: http://www.yiiframework.com/doc/guide/form.model#securing-attribute-assignments - http://www.yiiframework.com/doc/guide/form.model#triggering-validation - - -- The methods CActiveRecord::protectedAttributes() and CActiveRecord::safeAttributes() - are removed due to the introduction of the above feature. You may need - to adjust your code by changing the validation rules accordingly. Upgrading from v1.0.0 diff --git a/demos/blog/protected/models/LoginForm.php b/demos/blog/protected/models/LoginForm.php index 67a7706d86..2ac35cec81 100644 --- a/demos/blog/protected/models/LoginForm.php +++ b/demos/blog/protected/models/LoginForm.php @@ -23,8 +23,6 @@ public function rules() array('username, password', 'required'), // password needs to be authenticated array('password', 'authenticate'), - // rememberMe does not need validation. it can be massively assigned - array('rememberMe', 'safe'), ); } diff --git a/demos/blog/protected/models/Post.php b/demos/blog/protected/models/Post.php index 0c14fc95d0..4aee875a71 100644 --- a/demos/blog/protected/models/Post.php +++ b/demos/blog/protected/models/Post.php @@ -36,10 +36,17 @@ public function rules() array('title', 'length', 'max'=>128), array('title, content, status', 'required'), array('status', 'numerical', 'min'=>0, 'max'=>3), - array('tagInput', 'safe'), ); } + /** + * @return array attributes that can be massively assigned + */ + public function safeAttributes() + { + return 'title, content, status, tagInput'; + } + /** * @return array relational rules. */ @@ -122,8 +129,7 @@ protected function afterSave() { if(($tag=Tag::model()->findByAttributes(array('name'=>$name)))===null) { - $tag=new Tag; - $tag->name=$name; + $tag=new Tag(array('name'=>$name)); $tag->save(); } $this->dbConnection->createCommand("INSERT INTO PostTag (postId, tagId) VALUES ({$this->id},{$tag->id})")->execute(); diff --git a/demos/phonebook/protected/models/Contact.php b/demos/phonebook/protected/models/Contact.php index 50910a6507..17007aee73 100644 --- a/demos/phonebook/protected/models/Contact.php +++ b/demos/phonebook/protected/models/Contact.php @@ -22,11 +22,4 @@ public static function model($className=__CLASS__) { return parent::model($className); } - - public function rules() - { - return array( - array('id, name, phone', 'required'), - ); - } } \ No newline at end of file diff --git a/docs/guide/form.model.txt b/docs/guide/form.model.txt index 7a5dc0717f..8154c221ce 100644 --- a/docs/guide/form.model.txt +++ b/docs/guide/form.model.txt @@ -169,9 +169,6 @@ valid number. - `required`: alias of [CRequiredValidator], ensuring the attribute is not empty. - - `safe`: alias of [CSafeValidator], marking that the attribute is safe -to be assigned in a massive manner. - - `type`: alias of [CTypeValidator], ensuring the attribute is of specific data type. @@ -184,18 +181,22 @@ Below we list some examples of using the predefined validators: ~~~ [php] -// ensure password attribute is the same as password2 attribute -array('password', 'compare', 'compareAttribute'=>'password2') -// ensure username's length is between 3 and 12 -array('username', 'length', 'min'=>3, 'max'=>12) -// ensure age is a non-negative integer -array('age', 'numeric', 'integerOnly'=>true, 'min'=>0) +// username is required +array('username', 'required'), +// username must be between 3 and 12 characters +array('username', 'length', 'min'=>3, 'max'=>12), +// when in register scenario, password must match password2 +array('password', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'), +// when in login scenario, password must be authenticated +array('password', 'authenticate', 'on'=>'login'), ~~~ Securing Attribute Assignments ------------------------------ +> Note: scenario-based attribute assignment has been available since version 1.0.2. + After a model instance is created, we often need to populate its attributes with the data submitted by end-users. This can be done conveniently using the following massive assignment: @@ -221,49 +222,62 @@ foreach($_POST['LoginForm'] as $name=>$value) } ~~~ -The task of deciding whether a data entry is safe or not is based on -the validation rules that we just described. In particular, a data entry -is considered to be safe if it meets both of the following conditions: - - - The entry name appears in the attribute list of some validation rule - which applies to the current scenario. A validation rule applies to - a specified scenario if the `on` option of the rule is not set or - contains the specified scenario name. - - - The entry name is a valid model attribute. For [CFormModel], this means - the entry name must refer to a public member variable of the model class; - while for [CActiveRecord], this means the entry name must refer to either - a column of the associated DB table or a member variable of the class. - - -> Tip: If the scenario is empty, only the validation rules with empty `on` -> option will be applied. In this case, we can write the massive assignment -> statement as follows, -> -> ~~~ -> [php] -> $model->attributes=$_POST['LoginForm']; -> // equivalent to: $model->setAttributes($_POST['LoginForm']); -> ~~~ -> -> If an attribute does not need any validation but we still want to massively -> assign it, we can use the following `safe` validation rule to mark it: -> -> ~~~ -> [php] -> array('AttributeName', 'safe') -> ~~~ -> - -To assign a data entry that does not meet the first condition, we need to -use the following individual assignment: +The task of deciding whether a data entry is safe or not is based +the return value of a method named `safeAttributes` and the specified +scenario. By default, the method returns all public member variables +as safe attributes for [CFormModel], while it returns all table columns +except the primary key as safe attributes for [CActiveRecord]. We may +override this method to limit safe attributes according to scenarios. +For example, a user model may contain many attributes, but in `login` +scenario we only need to use `username` and `password` attributes. +We can specify this limit as follows: ~~~ [php] -$model->permission='admin'; +public function safeAttributes() +{ + return array( + parent::safeAttributes(), + 'login' => 'username, password', + ); +} ~~~ -> Note: scenario-based attribute assignment has been available since version 1.0.2. +More accurately, the return value of the `safeAttributes` method should be +of the following structure: + +~~~ +[php] +array( + // these attributes can be massively assigned in any scenario + // that is not explicitly specified below + 'attr1, attr2, ...', + * + // these attributes can be massively assigned only in scenario 1 + 'scenario1' => 'attr2, attr3, ...', + * + // these attributes can be massively assigned only in scenario 2 + 'scenario2' => 'attr1, attr3, ...', +) +~~~ + +If the model is not scenario-sensitive (i.e., it is only used +in one scenario, or all scenarios share the same set of safe attributes), +the return value can be simplified as a single string: + +~~~ +[php] +'attr1, attr2, ...' +~~~ + +For data entries that are not safe, we need to assign them to the corresponding +attributes using individual assign statements, like the following: + +~~~ +[php] +$model->permission='admin'; +$model->id=1; +~~~ Triggering Validation diff --git a/framework/YiiBase.php b/framework/YiiBase.php index 48256d55cd..73dbef8074 100644 --- a/framework/YiiBase.php +++ b/framework/YiiBase.php @@ -479,6 +479,7 @@ public static function t($category,$message,$params=array(),$source=null) 'CLogger' => '/logging/CLogger.php', 'CProfileLogRoute' => '/logging/CProfileLogRoute.php', 'CWebLogRoute' => '/logging/CWebLogRoute.php', + 'ControllerGenerator' => '/rad/controller/ControllerGenerator.php', 'CDateParser' => '/utils/CDateParser.php', 'CFileHelper' => '/utils/CFileHelper.php', 'CMarkdownParser' => '/utils/CMarkdownParser.php', @@ -494,7 +495,6 @@ public static function t($category,$message,$params=array(),$source=null) 'CRangeValidator' => '/validators/CRangeValidator.php', 'CRegularExpressionValidator' => '/validators/CRegularExpressionValidator.php', 'CRequiredValidator' => '/validators/CRequiredValidator.php', - 'CSafeValidator' => '/validators/CSafeValidator.php', 'CStringValidator' => '/validators/CStringValidator.php', 'CTypeValidator' => '/validators/CTypeValidator.php', 'CUniqueValidator' => '/validators/CUniqueValidator.php', diff --git a/framework/base/CModel.php b/framework/base/CModel.php index c404791590..4be73348b1 100644 --- a/framework/base/CModel.php +++ b/framework/base/CModel.php @@ -29,6 +29,100 @@ abstract class CModel extends CComponent * @since 1.0.1 */ abstract public function attributeNames(); + /** + * Returns the name of attributes that are safe to be massively assigned. + * For details about massive assignment, see the documentation of + * child classes (e.g. {@link CActiveRecord::setAttributes}, + * {@link CFormModel::setAttributes}). + * + * The returned value of this method should be in the following structure: + *
+	 * array(
+	 *    // these attributes can be massively assigned in any scenario
+	 *    // that is not explicitly specified below
+	 *    'attr1, attr2, ...',
+	 *
+	 *    // these attributes can be massively assigned only in scenario 1
+	 *    'scenario1' => 'attr2, attr3, ...',
+	 *
+	 *    // these attributes can be massively assigned only in scenario 2
+	 *    'scenario2' => 'attr1, attr3, ...',
+	 * );
+	 * 
+ * If the model is not scenario-sensitive (i.e., it is only used + * in one scenario, or all scenarios share the same set of safe attributes), + * the return value can be simplified as a single string: + *
+	 * 'attr1, attr2, ...'
+	 * 
+ * @return array list of safe attribute names. + * @since 1.0.2 + */ + abstract public function safeAttributes(); + + /** + * Returns the validation rules for attributes. + * + * This method should be overridden to declare validation rules. + * Each rule is an array with the following structure: + *
+	 * array('attribute list', 'validator name', 'on'=>'scenario name', ...validation parameters...)
+	 * 
+ * where + * + * + * The following are some examples: + *
+	 * array(
+	 *     array('username', 'required'),
+	 *     array('username', 'length', 'min'=>3, 'max'=>12),
+	 *     array('password', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'),
+	 *     array('password', 'authenticate', 'on'=>'login'),
+	 * );
+	 * 
+ * + * Note, in order to inherit rules defined in the parent class, a child class needs to + * merge the parent rules with child rules using functions like array_merge(). + * + * @return array validation rules to be applied when {@link validate()} is called. + */ + public function rules() + { + return array(); + } + + /** + * Returns the attribute labels. + * Attribute labels are mainly used in error messages of validation. + * By default an attribute label is generated using {@link generateAttributeLabel}. + * This method allows you to explicitly specify attribute labels. + * + * Note, in order to inherit labels defined in the parent class, a child class needs to + * merge the parent labels with child labels using functions like array_merge(). + * + * @return array attribute labels (name=>label) + * @see generateAttributeLabel + */ + public function attributeLabels() + { + return array(); + } /** * Performs the validation. @@ -36,17 +130,26 @@ abstract public function attributeNames(); * Errors found during the validation can be retrieved via {@link getErrors}. * @param string the scenario that the validation rules should be applied. * Defaults to empty string, meaning only those validation rules whose "on" - * property is not set will be applied. If this is a non-empty string, only - * the validation rules whose "on" property contains the specified scenario + * option is empty will be applied. If this is a non-empty string, only + * the validation rules whose "on" option is empty or contains the specified scenario * will be applied. * @param array list of attributes that should be validated. Defaults to null, * meaning any attribute listed in the applicable validation rules should be - * validated. If this parameter is given with a list of attributes, only + * validated. If this parameter is given as a list of attributes, only * the listed attributes will be validated. * @return boolean whether the validation is successful without any error. + * @see beforeValidate + * @see afterValidate */ public function validate($scenario='',$attributes=null) { + if(is_string($attributes)) + { + // backward compatible with 1.0.1 + $tmp=$scenario; + $scenario=$attributes; + $attributes=$tmp; + } $this->clearErrors(); if($this->beforeValidate($scenario)) { @@ -86,23 +189,6 @@ protected function afterValidate($scenario) { } - /** - * @return array validators built based on {@link rules()}. - */ - public function createValidators() - { - $validators=array(); - foreach($this->rules() as $rule) - { - if(isset($rule[0],$rule[1])) // attributes, validator name - $validators[]=CValidator::createValidator($rule[1],$this,$rule[0],array_slice($rule,2)); - else - throw new CException(Yii::t('yii','{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.', - array('{class}'=>get_class($this)))); - } - return $validators; - } - /** * @return array list of validators created according to {@link rules}. * @since 1.0.1 @@ -112,73 +198,12 @@ public function getValidators() return $this->createValidators(); } - /** - * Returns the attribute labels. - * Attribute labels are mainly used in error messages of validation. - * By default an attribute label is generated using {@link generateAttributeLabel}. - * This method allows you to explicitly specify attribute labels. - * - * Note, in order to inherit labels defined in the parent class, a child class needs to - * merge the parent labels with child labels using functions like array_merge(). - * - * @return array attribute labels (name=>label) - * @see generateAttributeLabel - */ - public function attributeLabels() - { - return array(); - } - - /** - * Returns the validation rules for attributes. - * - * This method should be overridden to declare validation rules. - * Each rule is an array with the following structure: - *
-	 * array('attribute list', 'validator name', 'on'=>'insert', ...validation parameters...)
-	 * 
- * where - * - * - * The following are some examples: - *
-	 * array(
-	 *     array('username', 'length', 'min'=>3, 'max'=>12),
-	 *     array('password', 'compare', 'compareAttribute'=>'password2'),
-	 *     array('password', 'authenticate'),
-	 * );
-	 * 
- * - * Note, in order to inherit rules defined in the parent class, a child class needs to - * merge the parent rules with child rules using functions like array_merge(). - * - * @return array validation rules to be applied when {@link validate()} is called. - */ - public function rules() - { - return array(); - } - /** * Returns the text label for the specified attribute. * @param string the attribute name * @return string the attribute label * @see generateAttributeLabel + * @see attributeLabels */ public function getAttributeLabel($attribute) { @@ -190,7 +215,7 @@ public function getAttributeLabel($attribute) } /** - * Returns a value indicating whether there is any error. + * Returns a value indicating whether there is any validation error. * @param string attribute name. Use null to check all attributes. * @return boolean whether there is any error. */ @@ -203,7 +228,7 @@ public function hasErrors($attribute=null) } /** - * Returns errors for all attribute or a single attribute. + * Returns the errors for all attribute or a single attribute. * @param string attribute name. Use null to retrieve errors for all attributes. * @return array errors for all attributes or the specified attribute. Empty array is returned if no error. */ @@ -215,6 +240,17 @@ public function getErrors($attribute=null) return isset($this->_errors[$attribute]) ? $this->_errors[$attribute] : array(); } + /** + * Returns the first error of the specified attribute. + * @param string attribute name. + * @return string the error message. Null is returned if no error. + * @since 1.0.2 + */ + public function getError($attribute) + { + return isset($this->_errors[$attribute]) ? reset($this->_errors[$attribute]) : null; + } + /** * Adds a new error to the specified attribute. * @param string attribute name @@ -237,6 +273,23 @@ public function clearErrors($attribute=null) unset($this->_errors[$attribute]); } + /** + * @return array validators built based on {@link rules()}. + */ + public function createValidators() + { + $validators=array(); + foreach($this->rules() as $rule) + { + if(isset($rule[0],$rule[1])) // attributes, validator name + $validators[]=CValidator::createValidator($rule[1],$this,$rule[0],array_slice($rule,2)); + else + throw new CException(Yii::t('yii','{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.', + array('{class}'=>get_class($this)))); + } + return $validators; + } + /** * Generates a user friendly attribute label. * This is done by replacing underscores or dashes with blanks and @@ -251,27 +304,105 @@ public function generateAttributeLabel($name) } /** - * Returns the attribute names that are safe to be massively assigned. - * An attribute is safe if it meets both of the following conditions: + * Returns all attribute values. + * @param array list of attributes whose value needs to be returned. + * Defaults to null, meaning all attributes as listed in {@link attributeNames} will be returned. + * If it is an array, only the attributes in the array will be returned. + * @return array attribute values (name=>value). + */ + public function getAttributes($names=null) + { + $values=array(); + foreach($this->attributeNames() as $name) + $values[$name]=$this->$name; + + if(is_array($names)) + { + $values2=array(); + foreach($names as $name) + $values2[$name]=isset($values[$name]) ? $values[$name] : null; + return $values2; + } + else + return $values; + } + + /** + * Sets the attribute values in a massive way. + * Only safe attributes will be assigned by this method. + * + * Given a scenario, this method will assign values to the attributes + * that appear in the list returned by {@link safeAttributes} for the specified scenario: * - * @param string scenario name - * @return array attribute names indexed by themselves - * @since 1.0.2 + * @param array attribute values (name=>value) to be set. + * @param mixed scenario name. + * @see getSafeAttributeNames */ - public function getSafeAttributeNames($scenario='') + public function setAttributes($values,$scenario='') { - $attributes=array(); - foreach($this->getValidators() as $validator) + if(is_array($values)) { - if($validator->applyTo($scenario)) + if($scenario===false) + $attributes=array_flip($this->attributeNames()); + else + $attributes=array_flip($this->getSafeAttributeNames($scenario)); + foreach($values as $name=>$value) { - foreach($validator->attributes as $name) - $attributes[$name]=$name; + if(isset($attributes[$name])) + $this->$name=$value; } } - return $attributes; + } + + /** + * Returns the attribute names that are safe to be massively assigned. + * This method is internally used by {@link setAttributes}. + * + * Given a scenario, this method will choose the folllowing result + * from the list returned by {@link safeAttributes}: + * + * @param string scenario name + * @return array safe attribute names + * @since 1.0.2 + */ + public function getSafeAttributeNames($scenario='') + { + if($scenario===false) + return $this->attributeNames(); + + $attributes=$this->safeAttributes(); + if(!is_array($attributes)) + $attributes=array($attributes); + + if($scenario!=='' && isset($attributes[$scenario])) + return $this->ensureArray($attributes[$scenario]); + + if(isset($attributes[0], $attributes[1])) + return $attributes; + else + return isset($attributes[0]) ? $this->ensureArray($attributes[0]) : array(); + } + + private function ensureArray($value) + { + return is_array($value) ? $value : preg_split('/[\s,]+/',$value,-1,PREG_SPLIT_NO_EMPTY); } } \ No newline at end of file diff --git a/framework/cli/views/shell/controller/controller.php b/framework/cli/views/shell/controller/controller.php index 95fea94154..e3caaf61a0 100644 --- a/framework/cli/views/shell/controller/controller.php +++ b/framework/cli/views/shell/controller/controller.php @@ -28,16 +28,5 @@ public function actions() ), ); } - - public function defaultAction() - { - // return the default action name - return 'anotherActionName'; - } - - public function missingAction($actionName) - { - // deal with unrecognized actions - } */ } \ No newline at end of file diff --git a/framework/db/ar/CActiveRecord.php b/framework/db/ar/CActiveRecord.php index 2784e907c6..383ac5c018 100644 --- a/framework/db/ar/CActiveRecord.php +++ b/framework/db/ar/CActiveRecord.php @@ -290,7 +290,7 @@ * When using a built-in validator class, you can use an alias name instead of the full class name. * For example, you can use "required" instead of "system.validators.CRequiredValidator". * For more details, see {@link CValidator}. - *
  • on: this specifies when the validation rule should be performed. Please see {@link CModel::validate} + *
  • on: this specifies the scenario when the validation rule should be performed. Please see {@link CModel::validate} * for more details about this option. NOTE: if the validation is triggered by the {@link save} call, * it will use 'insert' to indicate an insertion operation, and 'update' an updating operation. * You may thus specify a rule with the 'on' option so that it is applied only to insertion or updating.
  • @@ -319,6 +319,9 @@ abstract class CActiveRecord extends CModel * @see getDbConnection */ public static $db; + + private static $_models=array(); // class name => model + /** * @var boolean whether the record is new and should be inserted when calling {@link save}. * This property is automatically in constructor and {@link populateRecord}. @@ -327,7 +330,6 @@ abstract class CActiveRecord extends CModel */ public $isNewRecord=false; - private static $_models=array(); // class name => model private $_md; private $_attributes=array(); // attribute name => attribute value private $_related=array(); // attribute name => related objects @@ -339,6 +341,7 @@ abstract class CActiveRecord extends CModel * are subject to filtering via {@link setAttributes}. If this parameter is null, * nothing will be done in the constructor (this is internally used by {@link populateRecord}). * @param string scenario name. See {@link setAttributes} for more details about this parameter. + * This parameter has been available since version 1.0.2. * @see setAttributes */ public function __construct($attributes=array(),$scenario='') @@ -604,6 +607,25 @@ public function attributeNames() return array_keys($this->getMetaData()->columns); } + /** + * Returns the name of attributes that are safe to be massively assigned. + * The default implementation returns all table columns exception primary key(s). + * Child class may override this method to further limit the attributes that can be massively assigned. + * See {@link CModel::safeAttributes} on how to override this method. + * @return array list of safe attribute names. + * @see CModel::safeAttributes + */ + public function safeAttributes() + { + $attributes=array(); + foreach($this->getMetaData()->columns as $name=>$column) + { + if(!$column->isPrimaryKey) + $attributes[]=$name; + } + return $attributes; + } + /** * Returns the database connection used by active record. * By default, the "db" application component is used as the database connection. @@ -627,20 +649,6 @@ public function getDbConnection() } } - /** - * Returns the text label for the specified attribute. - * @param string the attribute name - * @return string the attribute label - * @see CModel::generateAttributeLabel - */ - public function getAttributeLabel($attribute) - { - if(($label=$this->getMetaData()->getAttributeLabel($attribute))!==null) - return $label; - else - return $this->generateAttributeLabel($attribute); - } - /** * @param string the relation name * @return CActiveRelation the named relation declared for this AR class. Null if the relation does not exist. @@ -743,19 +751,19 @@ public function addRelatedRecord($name,$record,$multiple) * Returns all column attribute values. * Note, related objects are not returned. * @param mixed names of attributes whose value needs to be returned. - * If this is true (default), then all attribute values will be returned, including + * If this is null (default), then all attribute values will be returned, including * those that are not loaded from DB (null will be returned for those attributes). - * If this is null, then all attributes except those that are not loaded from DB will be returned. + * If this is true, all attributes except those that are not loaded from DB will be returned. * @return array attribute values indexed by attribute names. */ - public function getAttributes($names=true) + public function getAttributes($names=null) { $attributes=$this->_attributes; foreach($this->getMetaData()->columns as $name=>$column) { if(property_exists($this,$name)) $attributes[$name]=$this->$name; - else if($names===true && !isset($attributes[$name])) + else if($names===null && !isset($attributes[$name])) $attributes[$name]=null; } if(is_array($names)) @@ -769,43 +777,6 @@ public function getAttributes($names=true) return $attributes; } - /** - * Sets the attribute values in a massive way. - * - * Only safe attributes will be assigned by this method. An attribute is safe if - * it meets both of the following conditions: - * - * - * See {@link CModel::rules rules} for more details about specifying validation rules. - * - * @param array attributes (name=>value) to be massively assigned. - * @param string scenario name. Defaults to empty string, meaning only attributes - * listed in those validation rules with empty "on" property can be massively assigned. - * If this is false, attributes listed in {@link attributeNames} and those member variables - * can be massively assigned. - */ - public function setAttributes($values,$scenario='') - { - if(is_array($values)) - { - $md=$this->getMetaData(); - $attributes=$scenario===false ? null : $md->getSafeAttributeNames($scenario); - foreach($values as $name=>$value) - { - if(is_array($attributes) && !isset($attributes[$name])) - continue; - if(property_exists($this,$name)) - $this->$name=$value; - else if(isset($md->columns[$name])) - $this->_attributes[$name]=$value; - } - } - } - /** * Saves the current record. * The record is inserted as a row into the database table if it is manually @@ -904,12 +875,12 @@ protected function afterFind() * If the table's primary key is auto-incremental and is null before insertion, * it will be populated with the actual value after insertion. * Note, validation is not performed in this method. You may call {@link validate} to perform the validation. - * @param array list of attributes that need to be saved. Defaults to null, + * @param array list of attributes that need to be saved. Defaults to true, * meaning all attributes that are loaded from DB will be saved. * @return boolean whether the attributes are valid and the record is inserted successfully. * @throws CException if the record is not new */ - public function insert($attributes=null) + public function insert($attributes=true) { if(!$this->isNewRecord) throw new CDbException(Yii::t('yii','The active record cannot be inserted to database because it is not new.')); @@ -938,12 +909,12 @@ public function insert($attributes=null) * Updates the row represented by this active record. * All loaded attributes will be saved to the database. * Note, validation is not performed in this method. You may call {@link validate} to perform the validation. - * @param array list of attributes that need to be saved. Defaults to null, + * @param array list of attributes that need to be saved. Defaults to true, * meaning all attributes that are loaded from DB will be saved. * @return boolean whether the update is successful * @throws CException if the record is new */ - public function update($attributes=null) + public function update($attributes=true) { if($this->isNewRecord) throw new CDbException(Yii::t('yii','The active record cannot be updated because it is new.')); @@ -957,6 +928,42 @@ public function update($attributes=null) return false; } + /** + * Saves a selected list of attributes. + * Unlike {@link save}, this method only saves the specified attributes + * of an existing row dataset. It thus has better performance. + * Note, this method does neither attribute filtering nor validation. + * So do not use this method with untrusted data (such as user posted data). + * You may consider the following alternative if you want to do so: + *
    +	 * $postRecord=Post::model()->findByPk($postID);
    +	 * $postRecord->attributes=$_POST['post'];
    +	 * $postRecord->save();
    +	 * 
    + * @param array attributes to be updated. Each element represents an attribute name + * or an attribute value indexed by its name. If the latter, the record's + * attribute will be changed accordingly before saving. + * @return boolean whether the update is successful + * @throws CException if the record is new or any database error + */ + public function saveAttributes($attributes) + { + if(!$this->isNewRecord) + { + $values=array(); + foreach($attributes as $name=>$value) + { + if(is_integer($name)) + $values[$value]=$this->$value; + else + $values[$name]=$this->$name=$value; + } + return $this->updateByPk($this->getPrimaryKey(),$values)>0; + } + else + throw new CDbException(Yii::t('yii','The active record cannot be updated because it is new.')); + } + /** * Deletes the row corresponding to this active record. * @return boolean whether the deletion is successful. @@ -1540,9 +1547,7 @@ class CActiveRecordMetaData public $attributeDefaults=array(); private $_model; - private $_attributeLabels; private $_validators; - private $_safeAttributes=array(); /** * Constructor. @@ -1575,17 +1580,6 @@ public function __construct($model) } } - /** - * @param string the attribute name - * @return string the explicitly defined attribute label. Null if the label is not defined. - */ - public function getAttributeLabel($attribute) - { - if($this->_attributeLabels===null) - $this->_attributeLabels=$this->_model->attributeLabels(); - return isset($this->_attributeLabels[$attribute]) ? $this->_attributeLabels[$attribute] : null; - } - /** * @return array list of validators */ @@ -1595,22 +1589,4 @@ public function getValidators() $this->_validators=$this->_model->createValidators(); return $this->_validators; } - - /** - * Returns the attribute names that are safe to be massively assigned. - * An attribute is safe if it meets both of the following conditions: - * - * @param string scenario name - * @return array attribute names indexed by themselves - * @since 1.0.2 - */ - public function getSafeAttributeNames($scenario='') - { - if(!isset($this->_safeAttributes[$scenario])) - $this->_safeAttributes[$scenario]=$this->_model->getSafeAttributeNames($scenario); - return $this->_safeAttributes[$scenario]; - } } diff --git a/framework/messages/config.php b/framework/messages/config.php index b747d16d39..e0c45a4223 100644 --- a/framework/messages/config.php +++ b/framework/messages/config.php @@ -6,7 +6,7 @@ return array( 'sourcePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..', 'messagePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'messages', - 'languages'=>array('zh_cn','zh_tw','de','es','sv','he','nl','pt','ru','it','fr'), + 'languages'=>array('zh_cn','zh_tw','de','es','sv','he','nl','pt','ru','it','fr','ja'), 'fileTypes'=>array('php'), 'exclude'=>array( '.svn', diff --git a/framework/messages/ja/yii.php b/framework/messages/ja/yii.php new file mode 100644 index 0000000000..99e0dd2b14 --- /dev/null +++ b/framework/messages/ja/yii.php @@ -0,0 +1,208 @@ + '', + 'Active Record requires a "db" CDbConnection application component.' => '', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => '', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => '', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => '', + 'Application base path "{path}" is not a valid directory.' => '', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => '', + 'Authorization item "{item}" has already been assigned to user "{user}".' => '', + 'CApcCache requires PHP apc extension to be loaded.' => '', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => '', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => '', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => '', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbCacheDependency.sql cannot be empty.' => '', + 'CDbCommand failed to execute the SQL statement: {error}' => '', + 'CDbCommand failed to prepare the SQL statement: {error}' => '', + 'CDbConnection does not support reading schema for {driver} database.' => '', + 'CDbConnection failed to open the DB connection: {error}' => '', + 'CDbConnection is inactive and cannot perform any DB operations.' => '', + 'CDbConnection.connectionString cannot be empty.' => '', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => '', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbLogRoute requires database table "{table}" to store log messages.' => '', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => '', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => '', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => '', + 'CDirectoryCacheDependency.directory cannot be empty.' => '', + 'CFileCacheDependency.fileName cannot be empty.' => '', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => '', + 'CFilterChain can only take objects implementing the IFilter interface.' => '', + 'CFlexWidget.baseUrl cannot be empty.' => '', + 'CFlexWidget.name cannot be empty.' => '', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => '', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => '', + 'CHttpRequest is unable to determine the entry script URL.' => '', + 'CHttpRequest is unable to determine the path info of the request.' => '', + 'CHttpRequest is unable to determine the request URI.' => '', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => '', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => '', + 'CHttpSession.savePath "{path}" is not a valid directory.' => '', + 'CMemCache requires PHP memcache extension to be loaded.' => '', + 'CMemCache server configuration must be an array.' => '', + 'CMemCache server configuration must have "host" value.' => '', + 'CMultiFileUpload.name is required.' => '', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => '', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => '', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => '', + 'CSecurityManager.encryptionKey cannot be empty.' => '', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => '', + 'CSecurityManager.validationKey cannot be empty.' => '', + 'CTypedList<{type}> can only hold objects of {type} class.' => '', + 'CUrlManager.UrlFormat must be either "path" or "get".' => '', + 'CXCache requires PHP XCache extension to be loaded.' => '', + 'Cache table "{tableName}" does not exist.' => '', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => '', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => '', + 'Cannot add "{name}" as a child of itself.' => '', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => '', + 'Either "{parent}" or "{child}" does not exist.' => '', + 'Error: Table "{table}" does not have a primary key.' => '', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => '', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => '', + 'Event "{class}.{event}" is not defined.' => '', + 'Failed to write the uploaded file "{file}" to disk.' => '', + 'File upload was stopped by extension.' => '', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => '', + 'Get a new code' => '', + 'Invalid MO file revision: {revision}.' => '', + 'Invalid MO file: {file} (magic: {magic}).' => '', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => '', + 'List data must be an array or an object implementing Traversable.' => '', + 'List index "{index}" is out of bound.' => '', + 'Login Required' => '', + 'Map data must be an array or an object implementing Traversable.' => '', + 'Missing the temporary folder to store the uploaded file "{file}".' => '', + 'No columns are being updated for table "{table}".' => '', + 'No counter columns are being updated for table "{table}".' => '', + 'Object configuration must be an array containing a "class" element.' => '', + 'Please fix the following input errors:' => '', + 'Property "{class}.{property}" is not defined.' => '', + 'Property "{class}.{property}" is read only.' => '', + 'Queue data must be an array or an object implementing Traversable.' => '', + 'Relation "{name}" is not defined in active record class "{class}".' => '', + 'Stack data must be an array or an object implementing Traversable.' => '', + 'Table "{table}" does not have a column named "{column}".' => '', + 'Table "{table}" does not have a primary key defined.' => '', + 'The "filter" property must be specified with a valid callback.' => '', + 'The "pattern" property must be specified with a valid regular expression.' => '', + 'The "view" property is required.' => '', + 'The CSRF token could not be verified.' => '', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => '', + 'The active record cannot be deleted because it is new.' => '', + 'The active record cannot be inserted to database because it is not new.' => '', + 'The active record cannot be updated because it is new.' => '', + 'The asset "{asset}" to be pulished does not exist.' => '', + 'The column "{column}" is not a foreign key in table "{table}".' => '', + 'The command path "{path}" is not a valid directory.' => '', + 'The controller path "{path}" is not a valid directory.' => '', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => '', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => '', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => '', + 'The file "{file}" was only partially uploaded.' => '', + 'The first element in a filter configuration must be the filter class.' => '', + 'The item "{name}" does not exist.' => '', + 'The item "{parent}" already has a child "{child}".' => '', + 'The layout path "{path}" is not a valid directory.' => '', + 'The list is read only.' => '', + 'The map is read only.' => '', + 'The pattern for 12 hour format must be "h" or "hh".' => '', + 'The pattern for 24 hour format must be "H" or "HH".' => '', + 'The pattern for AM/PM marker must be "a".' => '', + 'The pattern for day in month must be "F".' => '', + 'The pattern for day in year must be "D", "DD" or "DDD".' => '', + 'The pattern for day of the month must be "d" or "dd".' => '', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => '', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => '', + 'The pattern for hour in AM/PM must be "K" or "KK".' => '', + 'The pattern for hour in day must be "k" or "kk".' => '', + 'The pattern for minutes must be "m" or "mm".' => '', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => '', + 'The pattern for seconds must be "s" or "ss".' => '', + 'The pattern for time zone must be "z" or "v".' => '', + 'The pattern for week in month must be "W".' => '', + 'The pattern for week in year must be "w".' => '', + 'The queue is empty.' => '', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => '', + 'The requested controller "{controller}" does not exist.' => '', + 'The requested view "{name}" is not found.' => '', + 'The stack is empty.' => '', + 'The system is unable to find the requested action "{action}".' => '', + 'The system view path "{path}" is not a valid directory.' => '', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => '', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => '', + 'The verification code is incorrect.' => '', + 'The view path "{path}" is not a valid directory.' => '', + 'Theme directory "{directory}" does not exist.' => '', + 'This content requires the Adobe Flash Player.' => '', + 'Unable to add an item whose name is the same as an existing item.' => '', + 'Unable to change the item name. The name "{name}" is already used by another item.' => '', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => '', + 'Unable to find the decorator view "{view}".' => '', + 'Unable to find the list item.' => '', + 'Unable to lock file "{file}" for reading.' => '', + 'Unable to lock file "{file}" for writing.' => '', + 'Unable to read file "{file}".' => '', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => '', + 'Unable to write file "{file}".' => '', + 'Unknown authorization item "{name}".' => '', + 'Unrecognized locale "{locale}".' => '', + 'View file "{file}" does not exist.' => '', + 'Yii application can only be created once.' => '', + 'You are not authorized to perform this action.' => '', + 'Your request is not valid.' => '', + '{attribute} "{value}" has already been taken.' => '', + '{attribute} cannot be blank.' => '', + '{attribute} is invalid.' => '', + '{attribute} is not a valid URL.' => '', + '{attribute} is not a valid email address.' => '', + '{attribute} is not in the list.' => '', + '{attribute} is of the wrong length (should be {length} characters).' => '', + '{attribute} is too big (maximum is {max}).' => '', + '{attribute} is too long (maximum is {max} characters).' => '', + '{attribute} is too short (minimum is {min} characters).' => '', + '{attribute} is too small (minimum is {min}).' => '', + '{attribute} must be a number.' => '', + '{attribute} must be an integer.' => '', + '{attribute} must be repeated exactly.' => '', + '{attribute} must be {type}.' => '', + '{className} does not support add() functionality.' => '', + '{className} does not support delete() functionality.' => '', + '{className} does not support flush() functionality.' => '', + '{className} does not support get() functionality.' => '', + '{className} does not support set() functionality.' => '', + '{class} does not have attribute "{attribute}".' => '', + '{class} does not have attribute "{name}".' => '', + '{class} does not have relation "{name}".' => '', + '{class} does not support fetching all table names.' => '', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '', + '{class} must specify "model" and "attribute" or "name" property values.' => '', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '', + '{class}::authenticate() must be implemented.' => '', + '{controller} cannot find the requested view "{view}".' => '', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '', + '{controller} has an extra endWidget({id}) call in its view.' => '', + '{widget} cannot find the view "{view}".' => '', +); diff --git a/framework/validators/CSafeValidator.php b/framework/validators/CSafeValidator.php deleted file mode 100644 index 30e9ca9f66..0000000000 --- a/framework/validators/CSafeValidator.php +++ /dev/null @@ -1,32 +0,0 @@ - - * @link http://www.yiiframework.com/ - * @copyright Copyright © 2008-2009 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -/** - * CSafeValidator marks an attribute to be safe. - * CSafeValidator does not perform actual validation. It is mainly used - * to mark that an attribute is safe and can thus be massively assigned. - * - * @author Qiang Xue - * @version $Id$ - * @package system.validators - * @since 1.0 - */ -class CSafeValidator extends CValidator -{ - /** - * Validates the attribute of the object. - * If there is any error, the error message is added to the object. - * @param CModel the object being validated - * @param string the attribute being validated - */ - protected function validateAttribute($object,$attribute) - { - } -} diff --git a/framework/validators/CValidator.php b/framework/validators/CValidator.php index 72ef543a7f..693fc5895f 100644 --- a/framework/validators/CValidator.php +++ b/framework/validators/CValidator.php @@ -50,6 +50,25 @@ */ abstract class CValidator extends CComponent { + /** + * @var array list of built-in validators (name=>class) + */ + 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', + ); + /** * @var array list of attributes to be validated. */ @@ -85,23 +104,6 @@ abstract protected function validateAttribute($object,$attribute); */ public static function createValidator($name,$object,$attributes,$params) { - 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', - 'safe'=>'CSafeValidator', - ); - if(is_string($attributes)) $attributes=preg_split('/[\s,]+/',$attributes,-1,PREG_SPLIT_NO_EMPTY); @@ -125,8 +127,8 @@ public static function createValidator($name,$object,$attributes,$params) else { $params['attributes']=$attributes; - if(isset($builtInValidators[$name])) - $className=Yii::import($builtInValidators[$name],true); + if(isset(self::$builtInValidators[$name])) + $className=Yii::import(self::$builtInValidators[$name],true); else $className=Yii::import($name,true); $validator=new $className; diff --git a/framework/web/CController.php b/framework/web/CController.php index b954cd35f7..ea739f7561 100644 --- a/framework/web/CController.php +++ b/framework/web/CController.php @@ -644,7 +644,7 @@ public function renderDynamicInternal($callback,$params) */ public function createUrl($route,$params=array(),$ampersand='&') { - if(strpos($route,'/')===false) + if($route==='' || strpos($route,'/')===false) $route=$this->getId().'/'.($route==='' ? $this->getAction()->getId() : $route); return Yii::app()->createUrl($route,$params,$ampersand); } diff --git a/framework/web/CFormModel.php b/framework/web/CFormModel.php index 184e091354..b6f9788b6e 100644 --- a/framework/web/CFormModel.php +++ b/framework/web/CFormModel.php @@ -30,11 +30,29 @@ class CFormModel extends CModel * Constructor. * @param array initial attributes (name => value). The attributes * are subject to filtering via {@link setAttributes}. + * @param string scenario name. See {@link setAttributes} for more details about this parameter. + * This parameter has been available since version 1.0.2. + * @see setAttributes */ - public function __construct($attributes=array()) + public function __construct($attributes=array(),$scenario='') { if($attributes!==array()) - $this->setAttributes($attributes); + $this->setAttributes($attributes,$scenario); + } + + /** + * Returns the name of attributes that are safe to be massively assigned. + * The default implementation simply returns {@link attributeNames}. + * This method may be overridden by child classes. + * See {@link CModel::safeAttributes} for more details about how to + * override this method. + * @return array list of safe attribute names. + * @see CModel::safeAttributes + * @since 1.0.2 + */ + public function safeAttributes() + { + return $this->attributeNames(); } /** @@ -55,47 +73,4 @@ public function attributeNames() } return $names; } - - /** - * @return array all attribute values (name=>value). - * The attributes returned are those listed in {@link attributeNames}. - */ - public function getAttributes() - { - $values=array(); - foreach($this->attributeNames() as $name) - $values[$name]=$this->$name; - return $values; - } - - /** - * Sets the attribute values in a massive way. - * Only safe attributes will be assigned by this method. - * An attribute is safe if it meets both of the following conditions: - * - * - * @param array attribute values (name=>value) to be set. - * @param string scenario name. Defaults to empty string, meaning only attributes - * listed in those validation rules with empty "on" property can be massively assigned. - * If this is false, all attributes listed in {@link attributeNames} can be massively assigned. - */ - public function setAttributes($values,$scenario='') - { - if(is_array($values)) - { - if($scenario===false) - $attributes=array_flip($this->attributeNames()); - else - $attributes=$this->getSafeAttributeNames($scenario); - foreach($values as $name=>$value) - { - if(isset($attributes[$name])) - $this->$name=$value; - } - } - } } \ No newline at end of file diff --git a/framework/web/CWebApplication.php b/framework/web/CWebApplication.php index 193a951e00..36145d1a01 100644 --- a/framework/web/CWebApplication.php +++ b/framework/web/CWebApplication.php @@ -128,7 +128,7 @@ public function processRequest() protected function resolveRequest() { $route=$this->getUrlManager()->parseUrl($this->getRequest()); - if(($pos=strrpos($route,'/'))!==false) + if($route!=='' && ($pos=strrpos($route,'/'))!==false) return array(substr($route,0,$pos),(string)substr($route,$pos+1)); else return array($route,''); diff --git a/framework/web/auth/CAccessControlFilter.php b/framework/web/auth/CAccessControlFilter.php index 954087644f..93db7c9903 100644 --- a/framework/web/auth/CAccessControlFilter.php +++ b/framework/web/auth/CAccessControlFilter.php @@ -85,7 +85,7 @@ protected function preFilter($filterChain) $ip=$request->getUserHostAddress(); $action=$filterChain->action; - foreach($this->_rules as $rule) + foreach($this->getRules() as $rule) { if(($allow=$rule->isUserAllowed($user,$action,$ip,$verb))>0) // allowed break; @@ -167,12 +167,20 @@ public function isUserAllowed($user,$action,$ip,$verb) return 0; } - private function isActionMatched($action) + /** + * @param CAction the action + * @return boolean whether the rule applies to the action + */ + protected function isActionMatched($action) { return empty($this->actions) || in_array(strtolower($action->getId()),$this->actions); } - private function isUserMatched($user) + /** + * @param IWebUser the user + * @return boolean whether the rule applies to the user + */ + protected function isUserMatched($user) { if(empty($this->users)) return true; @@ -190,7 +198,11 @@ private function isUserMatched($user) return false; } - private function isRoleMatched($user) + /** + * @param string the role name + * @return boolean whether the rule applies to the role + */ + protected function isRoleMatched($user) { if(empty($this->roles)) return true; @@ -202,7 +214,11 @@ private function isRoleMatched($user) return false; } - private function isIpMatched($ip) + /** + * @param string the IP address + * @return boolean whether the rule applies to the IP address + */ + protected function isIpMatched($ip) { if(empty($this->ips)) return true; @@ -214,7 +230,11 @@ private function isIpMatched($ip) return false; } - private function isVerbMatched($verb) + /** + * @param string the request method + * @return boolean whether the rule applies to the request + */ + protected function isVerbMatched($verb) { return empty($this->verbs) || in_array(strtolower($verb),$this->verbs); } diff --git a/framework/web/auth/CWebUser.php b/framework/web/auth/CWebUser.php index 4bb6065c60..c5ba5e5015 100644 --- a/framework/web/auth/CWebUser.php +++ b/framework/web/auth/CWebUser.php @@ -348,11 +348,16 @@ public function clearStates() * A flash message is available only in the current and the next requests. * @param string key identifying the flash message * @param mixed value to be returned if the flash message is not available. + * @param boolean whether to delete this flash message after accessing it. + * Defaults to true. This parameter has been available since version 1.0.2. * @return mixed the message message */ - public function getFlash($key,$defaultValue=null) + public function getFlash($key,$defaultValue=null,$delete=true) { - return $this->getState(self::FLASH_KEY_PREFIX.$key,$defaultValue); + $value=$this->getState(self::FLASH_KEY_PREFIX.$key,$defaultValue); + if($delete) + $this->setFlash($key,null); + return $value; } /** @@ -380,7 +385,7 @@ public function setFlash($key,$value,$defaultValue=null) */ public function hasFlash($key) { - return $this->getFlash($key)!==null; + return $this->getFlash($key, null, false)!==null; } /** diff --git a/framework/web/helpers/CHtml.php b/framework/web/helpers/CHtml.php index 9ac38701c7..64c4ad0445 100644 --- a/framework/web/helpers/CHtml.php +++ b/framework/web/helpers/CHtml.php @@ -1086,7 +1086,10 @@ public static function errorSummary($model,$header='',$footer='') foreach($m->getErrors() as $errors) { foreach($errors as $error) - $content.="
  • $error
  • \n"; + { + if($error!='') + $content.="
  • $error
  • \n"; + } } } if($content!=='') @@ -1105,9 +1108,9 @@ public static function errorSummary($model,$header='',$footer='') */ public static function error($model,$attribute) { - $errors=$model->getErrors($attribute); - if(!empty($errors)) - return self::tag('div',array('class'=>self::$errorMessageCss),reset($errors)); + $error=$model->getError($attribute); + if($error!='') + return self::tag('div',array('class'=>self::$errorMessageCss),$error); else return ''; } diff --git a/framework/yiilite.php b/framework/yiilite.php index f882e79d4d..ae63106822 100644 --- a/framework/yiilite.php +++ b/framework/yiilite.php @@ -147,7 +147,7 @@ public static function getPathOfAlias($alias) } public static function setPathOfAlias($alias,$path) { - if($path===null) + if(empty($path)) unset(self::$_aliases[$alias]); else self::$_aliases[$alias]=rtrim($path,'\\/'); @@ -274,6 +274,7 @@ public static function t($category,$message,$params=array(),$source=null) 'CLogger' => '/logging/CLogger.php', 'CProfileLogRoute' => '/logging/CProfileLogRoute.php', 'CWebLogRoute' => '/logging/CWebLogRoute.php', + 'ControllerGenerator' => '/rad/controller/ControllerGenerator.php', 'CDateParser' => '/utils/CDateParser.php', 'CFileHelper' => '/utils/CFileHelper.php', 'CMarkdownParser' => '/utils/CMarkdownParser.php', @@ -289,7 +290,6 @@ public static function t($category,$message,$params=array(),$source=null) 'CRangeValidator' => '/validators/CRangeValidator.php', 'CRegularExpressionValidator' => '/validators/CRegularExpressionValidator.php', 'CRequiredValidator' => '/validators/CRequiredValidator.php', - 'CSafeValidator' => '/validators/CSafeValidator.php', 'CStringValidator' => '/validators/CStringValidator.php', 'CTypeValidator' => '/validators/CTypeValidator.php', 'CUniqueValidator' => '/validators/CUniqueValidator.php', @@ -1062,7 +1062,7 @@ public function processRequest() protected function resolveRequest() { $route=$this->getUrlManager()->parseUrl($this->getRequest()); - if(($pos=strrpos($route,'/'))!==false) + if($route!=='' && ($pos=strrpos($route,'/'))!==false) return array(substr($route,0,$pos),(string)substr($route,$pos+1)); else return array($route,''); @@ -1166,9 +1166,9 @@ public function createAbsoluteUrl($route,$params=array(),$schema='',$ampersand=' { return $this->getRequest()->getHostInfo($schema).$this->createUrl($route,$params,$ampersand); } - public function getBaseUrl() + public function getBaseUrl($absolute=false) { - return $this->getRequest()->getBaseUrl(); + return $this->getRequest()->getBaseUrl($absolute); } public function getHomeUrl() { @@ -1190,10 +1190,10 @@ public function createController($id) { if($id==='') $id=$this->defaultController; - if(!preg_match('/^\w+(\.\w+)*$/',$id)) - return null; if(isset($this->controllerMap[$id])) return Yii::createComponent($this->controllerMap[$id],$id); + if(!preg_match('/^\w+(\.\w+)*$/',$id)) + return null; if(($pos=strrpos($id,'.'))!==false) { $classFile=str_replace('.',DIRECTORY_SEPARATOR,$id).'Controller'; @@ -1655,7 +1655,8 @@ public function parseUrl($request) } } } - $route=$this->parseUrlDefault($pathInfo); + $r=$this->parseUrlDefault($pathInfo); + $route=isset($_GET[$this->routeVar])?$_GET[$this->routeVar]:$r; } else if(isset($_GET[$this->routeVar])) $route=$_GET[$this->routeVar]; @@ -1913,11 +1914,11 @@ public function setHostInfo($value) { $this->_hostInfo=rtrim($value,'/'); } - public function getBaseUrl() + public function getBaseUrl($absolute=false) { if($this->_baseUrl===null) $this->_baseUrl=rtrim(dirname($this->getScriptUrl()),'\\/'); - return $this->_baseUrl; + return $absolute ? $this->getHostInfo() . $this->_baseUrl : $this->_baseUrl; } public function setBaseUrl($value) { @@ -2462,6 +2463,8 @@ public function getViewFile($viewName) return $viewFile; if($viewName[0]==='/') $viewFile=Yii::app()->getViewPath().$viewName.'.php'; + else if(strpos($viewName,'.')) + $viewFile=Yii::getPathOfAlias($viewName).'.php'; else $viewFile=$this->getViewPath().DIRECTORY_SEPARATOR.$viewName.'.php'; return is_file($viewFile) ? Yii::app()->findLocalizedFile($viewFile) : false; @@ -2472,6 +2475,8 @@ public function getLayoutFile($layoutName) return $layoutFile; if($layoutName[0]==='/') $layoutFile=Yii::app()->getViewPath().$layoutName.'.php'; + else if(strpos($layoutName,'.')) + $layoutFile=Yii::getPathOfAlias($layoutName).'.php'; else $layoutFile=Yii::app()->getLayoutPath().DIRECTORY_SEPARATOR.$layoutName.'.php'; return is_file($layoutFile) ? Yii::app()->findLocalizedFile($layoutFile) : false; @@ -2485,9 +2490,9 @@ public function getClips() } public function render($view,$data=null,$return=false) { + $output=$this->renderPartial($view,$data,true); if(($layout=$this->layout)==null) $layout=Yii::app()->layout; - $output=$this->renderPartial($view,$data,true); if(!empty($layout) && ($layoutFile=$this->getLayoutFile($layout))!==false) $output=$this->renderFile($layoutFile,array('content'=>$output),true); $output=$this->processOutput($output); @@ -2543,7 +2548,7 @@ public function renderDynamicInternal($callback,$params) } public function createUrl($route,$params=array(),$ampersand='&') { - if(strpos($route,'/')===false) + if($route==='' || strpos($route,'/')===false) $route=$this->getId().'/'.($route==='' ? $this->getAction()->getId() : $route); return Yii::app()->createUrl($route,$params,$ampersand); } @@ -2849,9 +2854,12 @@ public function clearStates() { Yii::app()->getSession()->destroy(); } - public function getFlash($key,$defaultValue=null) + public function getFlash($key,$defaultValue=null,$delete=true) { - return $this->getState(self::FLASH_KEY_PREFIX.$key,$defaultValue); + $value=$this->getState(self::FLASH_KEY_PREFIX.$key,$defaultValue); + if($delete) + $this->setFlash($key,null); + return $value; } public function setFlash($key,$value,$defaultValue=null) { @@ -2865,7 +2873,7 @@ public function setFlash($key,$value,$defaultValue=null) } public function hasFlash($key) { - return $this->getFlash($key)!==null; + return $this->getFlash($key, null, false)!==null; } protected function changeIdentity($id,$name,$states) { @@ -3618,7 +3626,10 @@ public static function errorSummary($model,$header='',$footer='') foreach($m->getErrors() as $errors) { foreach($errors as $error) - $content.="
  • $error
  • \n"; + { + if($error!='') + $content.="
  • $error
  • \n"; + } } } if($content!=='') @@ -3628,9 +3639,9 @@ public static function errorSummary($model,$header='',$footer='') } public static function error($model,$attribute) { - $errors=$model->getErrors($attribute); - if(!empty($errors)) - return self::tag('div',array('class'=>self::$errorMessageCss),reset($errors)); + $error=$model->getError($attribute); + if($error!='') + return self::tag('div',array('class'=>self::$errorMessageCss),$error); else return ''; } @@ -3825,7 +3836,10 @@ public function getViewPath() } public function getViewFile($viewName) { - $viewFile=$this->getViewPath().DIRECTORY_SEPARATOR.$viewName.'.php'; + if(strpos($viewName,'.')) // a path alias + $viewFile=Yii::getPathOfAlias($viewName).'.php'; + else + $viewFile=$this->getViewPath().DIRECTORY_SEPARATOR.$viewName.'.php'; return is_file($viewFile) ? Yii::app()->findLocalizedFile($viewFile) : false; } public function render($view,$data=null,$return=false) @@ -4375,18 +4389,20 @@ class CInlineFilter extends CFilter public $name; public static function create($controller,$filterName) { - $filter=new CInlineFilter; - $filter->name=$filterName; - return $filter; + if(method_exists($controller,'filter'.$filterName)) + { + $filter=new CInlineFilter; + $filter->name=$filterName; + return $filter; + } + else + throw new CException(Yii::t('yii','Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".', + array('{filter}'=>$filterName, '{class}'=>get_class($controller)))); } public function filter($filterChain) { $method='filter'.$this->name; - if(method_exists($filterChain->controller,$method)) - $filterChain->controller->$method($filterChain); - else - throw new CException(Yii::t('yii','Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".', - array('{filter}'=>$this->name, '{class}'=>get_class($filterChain->controller)))); + $filterChain->controller->$method($filterChain); } } class CAccessControlFilter extends CFilter @@ -4418,7 +4434,7 @@ protected function preFilter($filterChain) $verb=$request->getRequestType(); $ip=$request->getUserHostAddress(); $action=$filterChain->action; - foreach($this->_rules as $rule) + foreach($this->getRules() as $rule) { if(($allow=$rule->isUserAllowed($user,$action,$ip,$verb))>0) // allowed break; @@ -4455,11 +4471,11 @@ public function isUserAllowed($user,$action,$ip,$verb) else return 0; } - private function isActionMatched($action) + protected function isActionMatched($action) { return empty($this->actions) || in_array(strtolower($action->getId()),$this->actions); } - private function isUserMatched($user) + protected function isUserMatched($user) { if(empty($this->users)) return true; @@ -4476,7 +4492,7 @@ private function isUserMatched($user) } return false; } - private function isRoleMatched($user) + protected function isRoleMatched($user) { if(empty($this->roles)) return true; @@ -4487,7 +4503,7 @@ private function isRoleMatched($user) } return false; } - private function isIpMatched($ip) + protected function isIpMatched($ip) { if(empty($this->ips)) return true; @@ -4498,7 +4514,7 @@ private function isIpMatched($ip) } return false; } - private function isVerbMatched($verb) + protected function isVerbMatched($verb) { return empty($this->verbs) || in_array(strtolower($verb),$this->verbs); } @@ -4507,6 +4523,15 @@ abstract class CModel extends CComponent { private $_errors=array(); // attribute name => array of errors abstract public function attributeNames(); + abstract public function safeAttributes(); + public function rules() + { + return array(); + } + public function attributeLabels() + { + return array(); + } public function validate($scenario='',$attributes=null) { $this->clearErrors(); @@ -4530,31 +4555,10 @@ protected function beforeValidate($scenario) protected function afterValidate($scenario) { } - public function createValidators() - { - $validators=array(); - foreach($this->rules() as $rule) - { - if(isset($rule[0],$rule[1])) // attributes, validator name - $validators[]=CValidator::createValidator($rule[1],$this,$rule[0],array_slice($rule,2)); - else - throw new CException(Yii::t('yii','{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.', - array('{class}'=>get_class($this)))); - } - return $validators; - } public function getValidators() { return $this->createValidators(); } - public function attributeLabels() - { - return array(); - } - public function rules() - { - return array(); - } public function getAttributeLabel($attribute) { $labels=$this->attributeLabels(); @@ -4577,6 +4581,10 @@ public function getErrors($attribute=null) else return isset($this->_errors[$attribute]) ? $this->_errors[$attribute] : array(); } + public function getError($attribute) + { + return isset($this->_errors[$attribute]) ? reset($this->_errors[$attribute]) : null; + } public function addError($attribute,$error) { $this->_errors[$attribute][]=$error; @@ -4588,22 +4596,70 @@ public function clearErrors($attribute=null) else unset($this->_errors[$attribute]); } + public function createValidators() + { + $validators=array(); + foreach($this->rules() as $rule) + { + if(isset($rule[0],$rule[1])) // attributes, validator name + $validators[]=CValidator::createValidator($rule[1],$this,$rule[0],array_slice($rule,2)); + else + throw new CException(Yii::t('yii','{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.', + array('{class}'=>get_class($this)))); + } + return $validators; + } public function generateAttributeLabel($name) { return ucwords(trim(strtolower(str_replace(array('-','_'),' ',preg_replace('/(?getValidators() as $validator) + $values=array(); + foreach($this->attributeNames() as $name) + $values[$name]=$this->$name; + if(is_array($names)) + { + $values2=array(); + foreach($names as $name) + $values2[$name]=isset($values[$name]) ? $values[$name] : null; + return $values2; + } + else + return $values; + } + public function setAttributes($values,$scenario='') + { + if(is_array($values)) { - if($validator->applyTo($scenario)) + if($scenario===false) + $attributes=array_flip($this->attributeNames()); + else + $attributes=array_flip($this->getSafeAttributeNames($scenario)); + foreach($values as $name=>$value) { - foreach($validator->attributes as $name) - $attributes[$name]=$name; + if(isset($attributes[$name])) + $this->$name=$value; } } - return $attributes; + } + public function getSafeAttributeNames($scenario='') + { + if($scenario===false) + return $this->attributeNames(); + $attributes=$this->safeAttributes(); + if(!is_array($attributes)) + $attributes=array($attributes); + if($scenario!=='' && isset($attributes[$scenario])) + return $this->ensureArray($attributes[$scenario]); + if(isset($attributes[0], $attributes[1])) + return $attributes; + else + return isset($attributes[0]) ? $this->ensureArray($attributes[0]) : array(); + } + private function ensureArray($value) + { + return is_array($value) ? $value : preg_split('/[\s,]+/',$value,-1,PREG_SPLIT_NO_EMPTY); } } abstract class CActiveRecord extends CModel @@ -4613,8 +4669,8 @@ abstract class CActiveRecord extends CModel const HAS_MANY='CHasManyRelation'; const MANY_MANY='CManyManyRelation'; public static $db; - public $isNewRecord=false; private static $_models=array(); // class name => model + public $isNewRecord=false; private $_md; private $_attributes=array(); // attribute name => attribute value private $_related=array(); // attribute name => related objects @@ -4726,6 +4782,16 @@ public function attributeNames() { return array_keys($this->getMetaData()->columns); } + public function safeAttributes() + { + $attributes=array(); + foreach($this->getMetaData()->columns as $name=>$column) + { + if(!$column->isPrimaryKey) + $attributes[]=$name; + } + return $attributes; + } public function getDbConnection() { if(self::$db!==null) @@ -4742,13 +4808,6 @@ public function getDbConnection() throw new CDbException(Yii::t('yii','Active Record requires a "db" CDbConnection application component.')); } } - public function getAttributeLabel($attribute) - { - if(($label=$this->getMetaData()->getAttributeLabel($attribute))!==null) - return $label; - else - return $this->generateAttributeLabel($attribute); - } public function getActiveRelation($name) { return isset($this->getMetaData()->relations[$name]) ? $this->getMetaData()->relations[$name] : null; @@ -4799,7 +4858,7 @@ public function addRelatedRecord($name,$record,$multiple) else if(!isset($this->_related[$name])) $this->_related[$name]=$record; } - public function getAttributes($names=true) + public function getAttributes($names=null) { $attributes=$this->_attributes; foreach($this->getMetaData()->columns as $name=>$column) @@ -4819,23 +4878,6 @@ public function getAttributes($names=true) else return $attributes; } - public function setAttributes($values,$scenario='') - { - if(is_array($values)) - { - $md=$this->getMetaData(); - $attributes=$scenario===false ? null : $md->getSafeAttributeNames($scenario); - foreach($values as $name=>$value) - { - if(is_array($attributes) && !isset($attributes[$name])) - continue; - if(property_exists($this,$name)) - $this->$name=$value; - else if(isset($md->columns[$name])) - $this->_attributes[$name]=$value; - } - } - } public function save($runValidation=true,$attributes=null) { if(!$runValidation || $this->validate($this->isNewRecord?'insert':'update', $attributes)) @@ -4897,9 +4939,9 @@ public function update($attributes=null) throw new CDbException(Yii::t('yii','The active record cannot be updated because it is new.')); if($this->beforeSave()) { - $result=$this->updateByPk($this->getPrimaryKey(),$this->getAttributes($attributes))>0; + $this->updateByPk($this->getPrimaryKey(),$this->getAttributes($attributes)); $this->afterSave(); - return $result; + return true; } else return false; @@ -5161,9 +5203,7 @@ class CActiveRecordMetaData public $relations=array(); public $attributeDefaults=array(); private $_model; - private $_attributeLabels; private $_validators; - private $_safeAttributes=array(); public function __construct($model) { $this->_model=$model; @@ -5187,24 +5227,12 @@ public function __construct($model) array('{class}'=>get_class($model),'{relation}'=>$name))); } } - public function getAttributeLabel($attribute) - { - if($this->_attributeLabels===null) - $this->_attributeLabels=$this->_model->attributeLabels(); - return isset($this->_attributeLabels[$attribute]) ? $this->_attributeLabels[$attribute] : null; - } public function getValidators() { if(!$this->_validators) $this->_validators=$this->_model->createValidators(); return $this->_validators; } - public function getSafeAttributeNames($scenario='') - { - if(!isset($this->_safeAttributes[$scenario])) - $this->_safeAttributes[$scenario]=$this->_model->getSafeAttributeNames($scenario); - return $this->_safeAttributes[$scenario]; - } } class CDbConnection extends CApplicationComponent { diff --git a/requirements/messages/config.php b/requirements/messages/config.php index 408aeb7c62..13f983411b 100644 --- a/requirements/messages/config.php +++ b/requirements/messages/config.php @@ -6,7 +6,7 @@ return array( 'sourcePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..', 'messagePath'=>dirname(__FILE__), - 'languages'=>array('zh_cn','zh_tw','de','es','sv','he','nl','pt','ru','it','fr'), + 'languages'=>array('zh_cn','zh_tw','de','es','sv','he','nl','pt','ru','it','fr','ja'), 'fileTypes'=>array('php'), 'translator'=>'t', 'exclude'=>array( diff --git a/requirements/messages/ja/yii.php b/requirements/messages/ja/yii.php new file mode 100644 index 0000000000..1f60573dde --- /dev/null +++ b/requirements/messages/ja/yii.php @@ -0,0 +1,44 @@ + '', + '$_SERVER variable' => '', + '$_SERVER["SCRIPT_FILENAME"] must be the same as the entry script file path.' => '', + 'APC extension' => '', + 'All DB-related classes' => '', + 'DOM extension' => '', + 'Either $_SERVER["REQUEST_URI"] or $_SERVER["QUERY_STRING"] must exist.' => '', + 'GD extension' => '', + 'Mcrypt extension' => '', + 'Memcache extension' => '', + 'PCRE extension' => '', + 'PDO MySQL extension' => '', + 'PDO PostgreSQL extension' => '', + 'PDO SQLite extension' => '', + 'PDO extension' => '', + 'PHP 5.1.0 or higher is required.' => '', + 'PHP version' => '', + 'Reflection extension' => '', + 'SOAP extension' => '', + 'SPL extension' => '', + 'This is required by encrypt and decrypt methods.' => '', + 'This is required if you are using MySQL database.' => '', + 'This is required if you are using PostgreSQL database.' => '', + 'This is required if you are using SQLite database.' => '', + 'Unable to determine URL path info. Please make sure $_SERVER["PATH_INFO"] (or $_SERVER["PHP_SELF"] and $_SERVER["SCRIPT_NAME"]) contains proper value.' => '', +); diff --git a/tests/ut/framework/db/data/models.php b/tests/ut/framework/db/data/models.php index 7f9eba8f35..056bff0b27 100644 --- a/tests/ut/framework/db/data/models.php +++ b/tests/ut/framework/db/data/models.php @@ -54,13 +54,6 @@ public function tableName() { return 'posts'; } - - public function rules() - { - return array( - array('title', 'safe'), - ); - } } class PostExt extends CActiveRecord diff --git a/tests/ut/framework/db/data/models2.php b/tests/ut/framework/db/data/models2.php index 2fdb39047c..faebed2dca 100644 --- a/tests/ut/framework/db/data/models2.php +++ b/tests/ut/framework/db/data/models2.php @@ -53,13 +53,6 @@ public function tableName() { return 'test.posts'; } - - public function rules() - { - return array( - array('title', 'safe'), - ); - } } class PostExt2 extends CActiveRecord