diff --git a/CHANGELOG b/CHANGELOG index fab4a6149..1df0e4b87 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -27,6 +27,7 @@ Version 1.0.3 to be released - New #138: Added support to specify additional attributes for OPTION tags (Qiang) - New #140: Allow the route in a URL rule to contain parameters by itself (Qiang) - New #146: Added CUrlManager.appendParams which allows creating URLs in path format whose GET parameters are all in the query part (Qiang) +- New #150: Register CTabView css file with media type='screen' (Qiang) - New #156: Added CUrlManager.cacheID to allow disabling URL rule caching (Qiang) - New: Upgraded jquery to 1.3.1 (Qiang) - New: Upgraded jquery star rating to 2.61 (Qiang) @@ -45,7 +46,7 @@ Version 1.0.3 to be released - New: Added support for auto-incremental composite primary keys in active record (Qiang) - New: Added support for application modules (Qiang) - New: Added "module" command for yiic shell tool (Qiang) - +- New: Added support for using default roles in RBAC (Qiang) Version 1.0.2 February 1, 2009 ------------------------------ diff --git a/docs/guide/basics.controller.txt b/docs/guide/basics.controller.txt index cb0a909d5..64d384f8c 100644 --- a/docs/guide/basics.controller.txt +++ b/docs/guide/basics.controller.txt @@ -52,6 +52,9 @@ and action. >class files are in lower case, and both [controller map|CWebApplication::controllerMap] >and [action map|CController::actions] are using keys in lower case. +Since version 1.0.3, an application can contain [modules](/doc/guide/basics.module). The route for a controller action inside a module is in the format of `moduleID/controllerID/actionID`. For more details, see the [section about modules](/doc/guide/basics.module). + + Controller Instantiation ------------------------ @@ -72,10 +75,12 @@ controller instance. - If the ID is in the format of `'path/to/xyz'`, the controller class name is assumed to be `XyzController` and the corresponding class file is `protected/controllers/path/to/XyzController.php`. For example, a controller -ID `admin.user` would be resolved as the controller class `UserController` +ID `admin/user` would be resolved as the controller class `UserController` and the class file `protected/controllers/admin/UserController.php`. If the class file does not exist, a 404 [CHttpException] will be raised. +In case when [modules](/doc/guide/basics.module) are used (available since version 1.0.3), the above process is slighly different. In particular, the application will check if the ID refers to a controller inside a module, and if so, the module instance will be created first followed by the controller instance. + Action ------ diff --git a/docs/guide/basics.module.txt b/docs/guide/basics.module.txt new file mode 100644 index 000000000..c3bd51ead --- /dev/null +++ b/docs/guide/basics.module.txt @@ -0,0 +1,93 @@ +Module +====== + +> Note: Support for module has been available since version 1.0.3. + +A module is a self-contained software unit that consists of [models](/doc/guide/basics.model), [views](/doc/guide/basics.view), [controllers](/doc/guide/basics.controller) and other supporting components. In many aspects, a module resembles to an [application](/doc/guide/basics.application). The main difference is that a module cannot be deployed alone and it must reside inside of an application. Users can access the controllers in a module like they do with normal application controllers. + +Modules are useful in several scenarios. For a large-scale application, we may divide it into several modules, each being developed and maintained separately. Some commonly used features, such as user management, comment management, may be developed in terms of modules so that they can be reused easily in future projects. + + +Creating Module +--------------- + +A module is organized as a directory whose name serves as its unique [ID|CWebModule::id]. The structure of the module directory is similar to that of the [application base directory](/doc/guide/basics.application#application-base-directory). The following shows the typical directory structure of a module named `forum`: + +~~~ +forum/ + ForumModule.php the module class file + components/ containing reusable user components + views/ containing view files for widgets + controllers/ containing controller class files + DefaultController.php the default controller class file + extensions/ containing third-party extensions + models/ containing model class files + views/ containing controller view and layout files + layouts/ containing layout view files + default/ containing view files for DefaultController + index.php the index view file +~~~ + +A module must have a module class that extends from [CWebModule]. The class name is determined using the expression `ucfirst($id).'Module'`, where `$id` refers to the module ID (or the module directory name). The module class serves as the central place for storing information shared among the module code. For example, we can use [CWebModule::params] to store module parameters, and use [CWebModule::components] to share [application components](/doc/guide/basics.application#application-component) at the module level. + +> Tip: We can use the `yiic` tool to create the basic skeleton of a new module. For example, to create the above `forum` module, we can execute the following commands in a command line window: +> +> ~~~ +> % cd WebRoot/testdrive +> % protected/yiic shell +> Yii Interactive Tool v1.0 +> Please type 'help' for help. Type 'exit' to quit. +> >> module forum +> ~~~ + + +Using Module +------------ + +To use a module, first place the module directory under `modules` of the [application base directory](/doc/guide/basics.application#application-base-directory). Then declare the module ID in the [modules|CWebApplication::modules] property of the application. For example, in order to use the above `forum` module, we can use the following [application configuration](/doc/guide/basics.application#application-configuration): + +~~~ +[php] +return array( + ...... + 'modules'=>array('forum',...), + ...... +); +~~~ + +A module can also be configured with initial property values. The usage is very similar to configuring [application components](/doc/guide/basics.application#application-component). For example, the `forum` module may have a property named `postPerPage` in its module class which can be configured in the [application configuration](/doc/guide/basics.application#application-configuration) as follows: + +~~~ +[php] +return array( + ...... + 'modules'=>array( + 'forum'=>array( + 'postPerPage'=>20, + ), + ), + ...... +); +~~~ + +The module instance may be accessed via the [module|CController::module] property of the currently active controller. Through the module instance, we can then access information that are shared at the module level. For example, in order to access the above `postPerPage` information, we can use the following expression: + +~~~ +[php] +$postPerPage=Yii::app()->controller->module->postPerPage; +// or the following if $this refers to the controller instance +// $postPerPage=$this->module->postPerPage; +~~~ + +A controller action in a module can be accessed using the [route](/doc/guide/basics.controller#route) `moduleID/controllerID/actionID`. For example, assuming the above `forum` module has a controller named `PostController`, we can use the [route](/doc/guide/basics.controller#route) `forum/post/create` to refer to the `create` action in this controller. The corresponding URL for this route would be `http://www.example.com/index.php?r=forum/post/create`. + +> Tip: If a controller is in a sub-directory of `controllers`, we can still use the above [route](/doc/guide/basics.controller#route) format. For example, assuming `PostController` is under `forum/controllers/admin`, we can refer to the `create` action using `forum/admin/post/create`. + + +Nested Module +------------- + +Modules can be nested. That is, a module can contain another module. We call the former *parent module* while the latter *child module*. Child modules must be placed under the `modules` directory of the parent module. To access a controller action in a child module, we should use the route `parentModuleID/childModuleID/controllerID/actionID`. + + +
+ * array( + * 'db'=>array( + * 'class'=>'CDbConnection', + * 'connectionString'=>'sqlite:path/to/file.db', + * ), + * 'cache'=>array( + * 'class'=>'CDbCache', + * 'connectionID'=>'db', + * 'enabled'=>!YII_DEBUG, // enable caching in non-debug mode + * ), + * ) + *+ * + * @param array module components(id=>component configuration or instances) + */ + public function setComponents($components) + { + foreach($components as $id=>$component) + { + if($component instanceof IApplicationComponent) + $this->setComponent($id,$component); + else if(isset($this->_componentConfig[$id])) + $this->_componentConfig[$id]=CMap::mergeArray($this->_componentConfig[$id],$component); + else + $this->_componentConfig[$id]=$component; + } + } + + /** + * Configures the module with the specified configuration. + * @param mixed the configuration array or a PHP script returning the configuration array. + */ + public function configure($config) + { + if(is_string($config)) + $config=require($config); + if(is_array($config)) + { + foreach($config as $key=>$value) + $this->$key=$value; + } + } } diff --git a/framework/web/auth/CAuthManager.php b/framework/web/auth/CAuthManager.php index 7f03ea05f..876cb4013 100644 --- a/framework/web/auth/CAuthManager.php +++ b/framework/web/auth/CAuthManager.php @@ -37,6 +37,19 @@ */ abstract class CAuthManager extends CApplicationComponent implements IAuthManager { + /** + * @var array list of role names that are assigned to all users implicitly. + * These roles do not need to be explicitly assigned to any user. + * When calling {@link checkAccess}, these roles will be checked first. + * For performance reason, you should minimize the number of such roles. + * A typical usage of such roles is to define an 'authenticated' role and associate + * it with a biz rule which checks if the current user is authenticated. + * And then declare 'authenticated' in this property so that it can be applied to + * every authenticated user. + * @since 1.0.3 + */ + public $defaultRoles=array(); + /** * Creates a role. * This is a shortcut method to {@link IAuthManager::createAuthItem}. diff --git a/framework/web/auth/CDbAuthManager.php b/framework/web/auth/CDbAuthManager.php index 0a77f0738..191a15923 100644 --- a/framework/web/auth/CDbAuthManager.php +++ b/framework/web/auth/CDbAuthManager.php @@ -81,6 +81,8 @@ class CDbAuthManager extends CAuthManager */ public function checkAccess($itemName,$userId,$params=array()) { + if(!empty($this->defaultRoles) && $this->checkDefaultRoles($itemName,$userId,$params)) + return true; $sql="SELECT name, type, description, t1.bizrule, t1.data, t2.bizrule AS bizrule2, t2.data AS data2 FROM {$this->itemTable} t1, {$this->assignmentTable} t2 WHERE name=itemname AND userid=:userid"; $command=$this->db->createCommand($sql); $command->bindValue(':userid',$userId); @@ -98,6 +100,43 @@ class CDbAuthManager extends CAuthManager return false; } + /** + * Checks the access based on the default roles as declared in {@link defaultRoles}. + * @param string the name of the operation that need access check + * @param mixed the user ID. This should can be either an integer and a string representing + * the unique identifier of a user. See {@link IWebUser::getId}. + * @param array name-value pairs that would be passed to biz rules associated + * with the tasks and roles assigned to the user. + * @return boolean whether the operations can be performed by the user according to the default roles. + * @since 1.0.3 + */ + protected function checkDefaultRoles($itemName,$userId,$params) + { + $names=array(); + foreach($this->defaultRoles as $role) + { + if(is_string($role)) + $names[]=$this->db->quoteValue($role); + else + $names[]=$role; + } + if(count($condition)<4) + $condition='name='.implode(' OR name=',$names); + else + $condition='name IN ('.implode(', ',$names).')'; + $sql="SELECT name, type, description, bizrule, data FROM {$this->itemTable} WHERE $condition"; + $command=$this->db->createCommand($sql); + $rows=$command->queryAll(); + + foreach($rows as $row) + { + $item=new CAuthItem($this,$row['name'],$row['type'],$row['description'],$row['bizrule'],unserialize($row['data'])); + if($item->checkAccess($itemName,$params)) + return true; + } + return false; + } + /** * Adds an item as a child of another item. * @param string the parent item name diff --git a/framework/web/auth/CPhpAuthManager.php b/framework/web/auth/CPhpAuthManager.php index 0e21f71af..b8328aafd 100644 --- a/framework/web/auth/CPhpAuthManager.php +++ b/framework/web/auth/CPhpAuthManager.php @@ -63,6 +63,8 @@ class CPhpAuthManager extends CAuthManager */ public function checkAccess($itemName,$userId,$params=array()) { + if(!empty($this->defaultRoles) && $this->checkDefaultRoles($itemName,$userId,$params)) + return true; foreach($this->getAuthAssignments($userId) as $assignment) { $item=$this->getAuthItem($assignment->getItemName()); @@ -72,6 +74,27 @@ class CPhpAuthManager extends CAuthManager return false; } + /** + * Checks the access based on the default roles as declared in {@link defaultRoles}. + * @param string the name of the operation that need access check + * @param mixed the user ID. This should can be either an integer and a string representing + * the unique identifier of a user. See {@link IWebUser::getId}. + * @param array name-value pairs that would be passed to biz rules associated + * with the tasks and roles assigned to the user. + * @return boolean whether the operations can be performed by the user according to the default roles. + * @since 1.0.3 + */ + protected function checkDefaultRoles($itemName,$userId,$params) + { + foreach($this->defaultRoles as $role) + { + $item=$this->getAuthItem($role); + if($item!==null && $item->checkAccess($itemName,$params)) + return true; + } + return false; + } + /** * Adds an item as a child of another item. * @param string the parent item name diff --git a/framework/web/widgets/CTabView.php b/framework/web/widgets/CTabView.php index 6583051e2..42584a183 100644 --- a/framework/web/widgets/CTabView.php +++ b/framework/web/widgets/CTabView.php @@ -163,7 +163,7 @@ class CTabView extends CWidget $cs=Yii::app()->getClientScript(); if($url===null) $url=$cs->getCoreScriptUrl().'/yiitab/jquery.yiitab.css'; - $cs->registerCssFile($url); + $cs->registerCssFile($url,'screen'); } /**