Welcome to the Cookbook

loading...

10.2.6 An Automated tool for creating ACOs

There is no translation yet for this section. Please help out and translate this.. More information about translations

As mentioned before, there is no pre-built way to input all of our controllers and actions into the Acl. However, we all hate doing repetitive things like typing in what could be hundreds of actions in a large application. We've whipped up an automated set of functions to build the ACO table. These functions will look at every controller in your application. It will add any non-private, non Controller methods to the Acl table, nicely nested underneath the owning controller. You can add and run this in your AppController or any controller for that matter, just be sure to remove it before putting your application into production.

	function build_acl() {
		if (!Configure::read('debug')) {
			return $this->_stop();
		}
		$log = array();

		$aco =& $this->Acl->Aco;
		$root = $aco->node('controllers');
		if (!$root) {
			$aco->create(array('parent_id' => null, 'model' => null, 'alias' => 'controllers'));
			$root = $aco->save();
			$root['Aco']['id'] = $aco->id; 
			$log[] = 'Created Aco node for controllers';
		} else {
			$root = $root[0];
		}   

		App::import('Core', 'File');
		$Controllers = Configure::listObjects('controller');
		$appIndex = array_search('App', $Controllers);
		if ($appIndex !== false ) {
			unset($Controllers[$appIndex]);
		}
		$baseMethods = get_class_methods('Controller');
		$baseMethods[] = 'buildAcl';

		$Plugins = $this->_getPluginControllerNames();
		$Controllers = array_merge($Controllers, $Plugins);

		// look at each controller in app/controllers
		foreach ($Controllers as $ctrlName) {
			$methods = $this->_getClassMethods($this->_getPluginControllerPath($ctrlName));

			// Do all Plugins First
			if ($this->_isPlugin($ctrlName)){
				$pluginNode = $aco->node('controllers/'.$this->_getPluginName($ctrlName));
				if (!$pluginNode) {
					$aco->create(array('parent_id' => $root['Aco']['id'], 'model' => null, 'alias' => $this->_getPluginName($ctrlName)));
					$pluginNode = $aco->save();
					$pluginNode['Aco']['id'] = $aco->id;
					$log[] = 'Created Aco node for ' . $this->_getPluginName($ctrlName) . ' Plugin';
				}
			}
			// find / make controller node
			$controllerNode = $aco->node('controllers/'.$ctrlName);
			if (!$controllerNode) {
				if ($this->_isPlugin($ctrlName)){
					$pluginNode = $aco->node('controllers/' . $this->_getPluginName($ctrlName));
					$aco->create(array('parent_id' => $pluginNode['0']['Aco']['id'], 'model' => null, 'alias' => $this->_getPluginControllerName($ctrlName)));
					$controllerNode = $aco->save();
					$controllerNode['Aco']['id'] = $aco->id;
					$log[] = 'Created Aco node for ' . $this->_getPluginControllerName($ctrlName) . ' ' . $this->_getPluginName($ctrlName) . ' Plugin Controller';
				} else {
					$aco->create(array('parent_id' => $root['Aco']['id'], 'model' => null, 'alias' => $ctrlName));
					$controllerNode = $aco->save();
					$controllerNode['Aco']['id'] = $aco->id;
					$log[] = 'Created Aco node for ' . $ctrlName;
				}
			} else {
				$controllerNode = $controllerNode[0];
			}

			//clean the methods. to remove those in Controller and private actions.
			foreach ($methods as $k => $method) {
				if (strpos($method, '_', 0) === 0) {
					unset($methods[$k]);
					continue;
				}
				if (in_array($method, $baseMethods)) {
					unset($methods[$k]);
					continue;
				}
				$methodNode = $aco->node('controllers/'.$ctrlName.'/'.$method);
				if (!$methodNode) {
					$aco->create(array('parent_id' => $controllerNode['Aco']['id'], 'model' => null, 'alias' => $method));
					$methodNode = $aco->save();
					$log[] = 'Created Aco node for '. $method;
				}
			}
		}
		if(count($log)>0) {
			debug($log);
		}
	}

	function _getClassMethods($ctrlName = null) {
		App::import('Controller', $ctrlName);
		if (strlen(strstr($ctrlName, '.')) > 0) {
			// plugin's controller
			$num = strpos($ctrlName, '.');
			$ctrlName = substr($ctrlName, $num+1);
		}
		$ctrlclass = $ctrlName . 'Controller';
		$methods = get_class_methods($ctrlclass);

		// Add scaffold defaults if scaffolds are being used
		$properties = get_class_vars($ctrlclass);
		if (array_key_exists('scaffold',$properties)) {
			if($properties['scaffold'] == 'admin') {
				$methods = array_merge($methods, array('admin_add', 'admin_edit', 'admin_index', 'admin_view', 'admin_delete'));
			} else {
				$methods = array_merge($methods, array('add', 'edit', 'index', 'view', 'delete'));
			}
		}
		return $methods;
	}

	function _isPlugin($ctrlName = null) {
		$arr = String::tokenize($ctrlName, '/');
		if (count($arr) > 1) {
			return true;
		} else {
			return false;
		}
	}

	function _getPluginControllerPath($ctrlName = null) {
		$arr = String::tokenize($ctrlName, '/');
		if (count($arr) == 2) {
			return $arr[0] . '.' . $arr[1];
		} else {
			return $arr[0];
		}
	}

	function _getPluginName($ctrlName = null) {
		$arr = String::tokenize($ctrlName, '/');
		if (count($arr) == 2) {
			return $arr[0];
		} else {
			return false;
		}
	}

	function _getPluginControllerName($ctrlName = null) {
		$arr = String::tokenize($ctrlName, '/');
		if (count($arr) == 2) {
			return $arr[1];
		} else {
			return false;
		}
	}

/**
 * Get the names of the plugin controllers ...
 * 
 * This function will get an array of the plugin controller names, and
 * also makes sure the controllers are available for us to get the 
 * method names by doing an App::import for each plugin controller.
 *
 * @return array of plugin names.
 *
 */
	function _getPluginControllerNames() {
		App::import('Core', 'File', 'Folder');
		$paths = Configure::getInstance();
		$folder =& new Folder();
		$folder->cd(APP . 'plugins');

		// Get the list of plugins
		$Plugins = $folder->read();
		$Plugins = $Plugins[0];
		$arr = array();

		// Loop through the plugins
		foreach($Plugins as $pluginName) {
			// Change directory to the plugin
			$didCD = $folder->cd(APP . 'plugins'. DS . $pluginName . DS . 'controllers');
			// Get a list of the files that have a file name that ends
			// with controller.php
			$files = $folder->findRecursive('.*_controller\.php');

			// Loop through the controllers we found in the plugins directory
			foreach($files as $fileName) {
				// Get the base file name
				$file = basename($fileName);

				// Get the controller name
				$file = Inflector::camelize(substr($file, 0, strlen($file)-strlen('_controller.php')));
				if (!preg_match('/^'. Inflector::humanize($pluginName). 'App/', $file)) {
					if (!App::import('Controller', $pluginName.'.'.$file)) {
						debug('Error importing '.$file.' for plugin '.$pluginName);
					} else {
						/// Now prepend the Plugin name ...
						// This is required to allow us to fetch the method names.
						$arr[] = Inflector::humanize($pluginName) . "/" . $file;
					}
				}
			}
		}
		return $arr;
	}
  1. function build_acl() {
  2. if (!Configure::read('debug')) {
  3. return $this->_stop();
  4. }
  5. $log = array();
  6. $aco =& $this->Acl->Aco;
  7. $root = $aco->node('controllers');
  8. if (!$root) {
  9. $aco->create(array('parent_id' => null, 'model' => null, 'alias' => 'controllers'));
  10. $root = $aco->save();
  11. $root['Aco']['id'] = $aco->id;
  12. $log[] = 'Created Aco node for controllers';
  13. } else {
  14. $root = $root[0];
  15. }
  16. App::import('Core', 'File');
  17. $Controllers = Configure::listObjects('controller');
  18. $appIndex = array_search('App', $Controllers);
  19. if ($appIndex !== false ) {
  20. unset($Controllers[$appIndex]);
  21. }
  22. $baseMethods = get_class_methods('Controller');
  23. $baseMethods[] = 'buildAcl';
  24. $Plugins = $this->_getPluginControllerNames();
  25. $Controllers = array_merge($Controllers, $Plugins);
  26. // look at each controller in app/controllers
  27. foreach ($Controllers as $ctrlName) {
  28. $methods = $this->_getClassMethods($this->_getPluginControllerPath($ctrlName));
  29. // Do all Plugins First
  30. if ($this->_isPlugin($ctrlName)){
  31. $pluginNode = $aco->node('controllers/'.$this->_getPluginName($ctrlName));
  32. if (!$pluginNode) {
  33. $aco->create(array('parent_id' => $root['Aco']['id'], 'model' => null, 'alias' => $this->_getPluginName($ctrlName)));
  34. $pluginNode = $aco->save();
  35. $pluginNode['Aco']['id'] = $aco->id;
  36. $log[] = 'Created Aco node for ' . $this->_getPluginName($ctrlName) . ' Plugin';
  37. }
  38. }
  39. // find / make controller node
  40. $controllerNode = $aco->node('controllers/'.$ctrlName);
  41. if (!$controllerNode) {
  42. if ($this->_isPlugin($ctrlName)){
  43. $pluginNode = $aco->node('controllers/' . $this->_getPluginName($ctrlName));
  44. $aco->create(array('parent_id' => $pluginNode['0']['Aco']['id'], 'model' => null, 'alias' => $this->_getPluginControllerName($ctrlName)));
  45. $controllerNode = $aco->save();
  46. $controllerNode['Aco']['id'] = $aco->id;
  47. $log[] = 'Created Aco node for ' . $this->_getPluginControllerName($ctrlName) . ' ' . $this->_getPluginName($ctrlName) . ' Plugin Controller';
  48. } else {
  49. $aco->create(array('parent_id' => $root['Aco']['id'], 'model' => null, 'alias' => $ctrlName));
  50. $controllerNode = $aco->save();
  51. $controllerNode['Aco']['id'] = $aco->id;
  52. $log[] = 'Created Aco node for ' . $ctrlName;
  53. }
  54. } else {
  55. $controllerNode = $controllerNode[0];
  56. }
  57. //clean the methods. to remove those in Controller and private actions.
  58. foreach ($methods as $k => $method) {
  59. if (strpos($method, '_', 0) === 0) {
  60. unset($methods[$k]);
  61. continue;
  62. }
  63. if (in_array($method, $baseMethods)) {
  64. unset($methods[$k]);
  65. continue;
  66. }
  67. $methodNode = $aco->node('controllers/'.$ctrlName.'/'.$method);
  68. if (!$methodNode) {
  69. $aco->create(array('parent_id' => $controllerNode['Aco']['id'], 'model' => null, 'alias' => $method));
  70. $methodNode = $aco->save();
  71. $log[] = 'Created Aco node for '. $method;
  72. }
  73. }
  74. }
  75. if(count($log)>0) {
  76. debug($log);
  77. }
  78. }
  79. function _getClassMethods($ctrlName = null) {
  80. App::import('Controller', $ctrlName);
  81. if (strlen(strstr($ctrlName, '.')) > 0) {
  82. // plugin's controller
  83. $num = strpos($ctrlName, '.');
  84. $ctrlName = substr($ctrlName, $num+1);
  85. }
  86. $ctrlclass = $ctrlName . 'Controller';
  87. $methods = get_class_methods($ctrlclass);
  88. // Add scaffold defaults if scaffolds are being used
  89. $properties = get_class_vars($ctrlclass);
  90. if (array_key_exists('scaffold',$properties)) {
  91. if($properties['scaffold'] == 'admin') {
  92. $methods = array_merge($methods, array('admin_add', 'admin_edit', 'admin_index', 'admin_view', 'admin_delete'));
  93. } else {
  94. $methods = array_merge($methods, array('add', 'edit', 'index', 'view', 'delete'));
  95. }
  96. }
  97. return $methods;
  98. }
  99. function _isPlugin($ctrlName = null) {
  100. $arr = String::tokenize($ctrlName, '/');
  101. if (count($arr) > 1) {
  102. return true;
  103. } else {
  104. return false;
  105. }
  106. }
  107. function _getPluginControllerPath($ctrlName = null) {
  108. $arr = String::tokenize($ctrlName, '/');
  109. if (count($arr) == 2) {
  110. return $arr[0] . '.' . $arr[1];
  111. } else {
  112. return $arr[0];
  113. }
  114. }
  115. function _getPluginName($ctrlName = null) {
  116. $arr = String::tokenize($ctrlName, '/');
  117. if (count($arr) == 2) {
  118. return $arr[0];
  119. } else {
  120. return false;
  121. }
  122. }
  123. function _getPluginControllerName($ctrlName = null) {
  124. $arr = String::tokenize($ctrlName, '/');
  125. if (count($arr) == 2) {
  126. return $arr[1];
  127. } else {
  128. return false;
  129. }
  130. }
  131. /**
  132. * Get the names of the plugin controllers ...
  133. *
  134. * This function will get an array of the plugin controller names, and
  135. * also makes sure the controllers are available for us to get the
  136. * method names by doing an App::import for each plugin controller.
  137. *
  138. * @return array of plugin names.
  139. *
  140. */
  141. function _getPluginControllerNames() {
  142. App::import('Core', 'File', 'Folder');
  143. $paths = Configure::getInstance();
  144. $folder =& new Folder();
  145. $folder->cd(APP . 'plugins');
  146. // Get the list of plugins
  147. $Plugins = $folder->read();
  148. $Plugins = $Plugins[0];
  149. $arr = array();
  150. // Loop through the plugins
  151. foreach($Plugins as $pluginName) {
  152. // Change directory to the plugin
  153. $didCD = $folder->cd(APP . 'plugins'. DS . $pluginName . DS . 'controllers');
  154. // Get a list of the files that have a file name that ends
  155. // with controller.php
  156. $files = $folder->findRecursive('.*_controller\.php');
  157. // Loop through the controllers we found in the plugins directory
  158. foreach($files as $fileName) {
  159. // Get the base file name
  160. $file = basename($fileName);
  161. // Get the controller name
  162. $file = Inflector::camelize(substr($file, 0, strlen($file)-strlen('_controller.php')));
  163. if (!preg_match('/^'. Inflector::humanize($pluginName). 'App/', $file)) {
  164. if (!App::import('Controller', $pluginName.'.'.$file)) {
  165. debug('Error importing '.$file.' for plugin '.$pluginName);
  166. } else {
  167. /// Now prepend the Plugin name ...
  168. // This is required to allow us to fetch the method names.
  169. $arr[] = Inflector::humanize($pluginName) . "/" . $file;
  170. }
  171. }
  172. }
  173. }
  174. return $arr;
  175. }

Now run the action in your browser, eg. http://localhost/groups/build_acl, This will build your ACO table.

You might want to keep this function around as it will add new ACO's for all of the controllers & actions that are in your application any time you run it. It does not remove nodes for actions that no longer exist though. Now that all the heavy lifting is done, we need to set up some permissions, and remove the code that disabled AuthComponent earlier.

The original code on this page did not take into account that you might use plugins for your application, and in order for you to have seamless plugin support in your Acl-controlled application, we have updated the above code to automatically include the correct plugins wherever necessary. Note that running this action will place some debug statements at the top of your browser page as to what Plugin/Controller/Action was added to the ACO tree and what was not.