Seditio Source
Root |
./othercms/croogo-4.0.7/vendor/cakephp/acl/src/AclExtras.php
<?php
/**
 * Acl Extras.
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright Copyright 2008-2013, Mark Story.
 * @link http://mark-story.com
 * @author Mark Story <mark@mark-story.com>
 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
 */
namespace Acl;

use
Acl\Controller\Component\AclComponent;
use
Cake\Console\Shell;
use
Cake\Controller\ComponentRegistry;
use
Cake\Controller\Controller;
use
Cake\Core\App;
use
Cake\Core\Configure;
use
Cake\Core\Plugin;
use
Cake\Filesystem\Folder;
use
Cake\Http\ServerRequest;
use
Cake\Routing\Router;
use
Cake\Utility\Hash;
use
Cake\Utility\Inflector;

/**
 * Provides features for additional ACL operations.
 * Can be used in either a CLI or Web context.
 */
class AclExtras
{

   
/**
     * Contains instance of AclComponent
     *
     * @var \Acl\Controller\Component\AclComponent
     */
   
public $Acl;

   
/**
     * Contains arguments parsed from the command line.
     *
     * @var array
     */
   
public $args;

   
/**
     * Contains database source to use
     *
     * @var string
     */
   
public $dataSource = 'default';

   
/**
     * Root node name.
     *
     * @var string
     */
   
public $rootNode = 'controllers';

   
/**
     * Internal Clean Actions switch
     *
     * @var bool
     */
   
protected $_clean = false;

   
/**
     * Contains app route prefixes
     *
     * @var array
     */
   
protected $prefixes = [];

   
/**
     * Contains plugins route prefixes
     *
     * @var array
     */
   
protected $pluginPrefixes = [];

   
/**
     * List of ACOs found during synchronization
     *
     * @var array
     */
   
protected $foundACOs = [];

   
/** @var \Cake\Controller\Controller */
   
protected $controller;

   
/** @var \Cake\Console\Shell */
   
protected $Shell;

   
/**
     * Start up And load Acl Component / Aco model
     *
     * @param \Cake\Controller\Controller $controller Controller instance
     * @return void
     */
   
public function startup($controller = null)
    {
        if (!
$controller) {
           
$controller = new Controller(new ServerRequest());
        }
       
$registry = new ComponentRegistry();
       
$this->Acl = new AclComponent($registry, Configure::read('Acl'));
       
$this->Aco = $this->Acl->Aco;
       
$this->controller = $controller;
       
$this->_buildPrefixes();
    }

   
/**
     * Output a message.
     *
     * Will either use shell->out, or controller->Flash->success()
     *
     * @param string $msg The message to output.
     * @return void
     */
   
public function out($msg)
    {
        if (!empty(
$this->controller->Flash)) {
           
$this->controller->Flash->success($msg);
        } else {
           
$this->Shell->out($msg);
        }
    }

   
/**
     * Output an error message.
     *
     * Will either use shell->err, or controller->Flash->error()
     *
     * @param string $msg The message to output.
     * @return void
     */
   
public function err($msg)
    {
        if (!empty(
$this->controller->Flash)) {
           
$this->controller->Flash->error($msg);
        } else {
           
$this->Shell->err($msg);
        }
    }

   
/**
     * Sync the ACO table
     *
     * @param array $params An array of parameters
     * @return void
     */
   
public function acoSync($params = [])
    {
       
$this->_clean = true;
       
$this->acoUpdate($params);
    }

   
/**
     * Updates the Aco Tree with new controller actions.
     *
     * @param array $params An array of parameters
     * @return bool
     */
   
public function acoUpdate($params = [])
    {
       
$root = $this->_checkNode($this->rootNode, $this->rootNode, null);
        if (empty(
$params['plugin'])) {
           
$plugins = Plugin::loaded();
           
$this->_processControllers($root);
           
$this->_processPrefixes($root);
           
$this->_processPlugins($root, $plugins);
        } else {
           
$plugin = $params['plugin'];
            if (!
Plugin::loaded($plugin)) {
               
$this->err(__d('cake_acl', "<error>Plugin {0} not found or not activated.</error>", [$plugin]));

                return
false;
            }
           
$plugins = [$params['plugin']];
           
$this->_processPlugins($root, $plugins);
           
$this->foundACOs = array_slice($this->foundACOs, 1, null, true);
        }

        if (
$this->_clean) {
            foreach (
$this->foundACOs as $parentId => $acosList) {
               
$this->_cleaner($parentId, $acosList);
            }
        }
       
$this->out(__d('cake_acl', '<success>Aco Update Complete</success>'));

        return
true;
    }

   
/**
     * Updates the Aco Tree with all App controllers.
     *
     * @param \Acl\Model\Entity\Aco $root The root node of Aco Tree
     * @return void
     */
   
protected function _processControllers($root)
    {
       
$controllers = $this->getControllerList();
       
$this->foundACOs[$root->id] = $this->_updateControllers($root, $controllers);
    }

   
/**
     * Updates the Aco Tree with all App route prefixes.
     *
     * @param \Acl\Model\Entity\Aco $root The root node of Aco Tree
     * @return void
     */
   
protected function _processPrefixes($root)
    {
        foreach (
array_keys($this->getPrefixes()) as $prefix) {
           
$controllers = $this->getControllerList(null, $prefix);
           
$path = $this->rootNode . '/' . $prefix;
           
$pathNode = $this->_checkNode($path, $prefix, $root->id);
           
$this->foundACOs[$root->id][] = $prefix;
            if (isset(
$this->foundACOs[$pathNode->id])) {
               
$this->foundACOs[$pathNode->id] += $this->_updateControllers($pathNode, $controllers, null, $prefix);
            } else {
               
$this->foundACOs[$pathNode->id] = $this->_updateControllers($pathNode, $controllers, null, $prefix);
            }
        }
    }

   
/**
     * Returns the aliased name for the plugin (Needed in order to correctly handle nested plugins)
     *
     * @param string $plugin The name of the plugin to alias
     * @return string
     */
   
protected function _pluginAlias($plugin)
    {
        return
preg_replace('/\//', '\\', Inflector::camelize($plugin));
    }

   
/**
     * Updates the Aco Tree with all Plugins.
     *
     * @param \Acl\Model\Entity\Aco $root The root node of Aco Tree
     * @param array $plugins list of App plugins
     * @return void
     */
   
protected function _processPlugins($root, array $plugins = [])
    {
        foreach (
$plugins as $plugin) {
           
$controllers = $this->getControllerList($plugin);
           
$pluginAlias = $this->_pluginAlias($plugin);
           
$path = [
               
$this->rootNode,
               
$pluginAlias
           
];
           
$path = implode('/', Hash::filter($path));
           
$pathNode = $this->_checkNode($path, $pluginAlias, $root->id);
           
$this->foundACOs[$root->id][] = $pluginAlias;

            if (isset(
$this->foundACOs[$pathNode->id])) {
               
$this->foundACOs[$pathNode->id] += $this->_updateControllers($pathNode, $controllers, $plugin);
            } else {
               
$this->foundACOs[$pathNode->id] = $this->_updateControllers($pathNode, $controllers, $plugin);
            }

            if (isset(
$this->pluginPrefixes[$plugin])) {
                foreach (
array_keys($this->pluginPrefixes[$plugin]) as $prefix) {
                   
$path = [
                       
$this->rootNode,
                       
$pluginAlias
                   
];
                   
$path = implode('/', Hash::filter($path));
                   
$pluginNode = $this->_checkNode($path, $pluginAlias, $root->id);
                   
$this->foundACOs[$root->id][] = $pluginAlias;

                   
$path = [
                       
$this->rootNode,
                       
$pluginAlias,
                       
$prefix,
                    ];
                   
$path = implode('/', Hash::filter($path));
                   
$pathNode = $this->_checkNode($path, $prefix, $pluginNode->id);
                   
$this->foundACOs[$pluginNode->id][] = $prefix;

                   
$controllers = $this->getControllerList($plugin, $prefix);
                    if (isset(
$this->foundACOs[$pathNode->id])) {
                       
$this->foundACOs[$pathNode->id] += $this->_updateControllers($pathNode, $controllers, $pluginAlias, $prefix);
                    } else {
                       
$this->foundACOs[$pathNode->id] = $this->_updateControllers($pathNode, $controllers, $pluginAlias, $prefix);
                    }
                }
            }
        }
    }

   
/**
     * Updates a collection of controllers.
     *
     * @param array $root Array or ACO information for root node.
     * @param array $controllers Array of Controllers
     * @param string $plugin Name of the plugin you are making controllers for.
     * @param string $prefix Name of the prefix you are making controllers for.
     * @return array
     */
   
protected function _updateControllers($root, $controllers, $plugin = null, $prefix = null)
    {
       
$pluginPath = $this->_pluginAlias($plugin);

       
// look at each controller
       
$controllersNames = [];
        foreach (
$controllers as $controller) {
           
$tmp = explode('/', $controller);
           
$controllerName = str_replace('Controller.php', '', array_pop($tmp));
           
// Always skip the App controller
           
if ($controllerName == 'App') {
                continue;
            }
           
// Skip anything that is not a concrete controller
           
$namespace = $this->_getNamespace($controller, $pluginPath, $prefix);
            if (!(new \
ReflectionClass($namespace))->isInstantiable()) {
                continue;
            }
           
$controllersNames[] = $controllerName;
           
$path = [
               
$this->rootNode,
               
$pluginPath,
               
$prefix,
               
$controllerName
           
];
           
$path = implode('/', Hash::filter($path));
           
$controllerNode = $this->_checkNode($path, $controllerName, $root->id);
           
$this->_checkMethods($controller, $controllerName, $controllerNode, $pluginPath, $prefix);
        }

        return
$controllersNames;
    }

   
/**
     * Get a list of controllers in the app and plugins.
     *
     * Returns an array of path => import notation.
     *
     * @param string $plugin Name of plugin to get controllers for
     * @param string $prefix Name of prefix to get controllers for
     * @return array
     */
   
public function getControllerList($plugin = null, $prefix = null)
    {
        if (!
$plugin) {
           
$path = App::path('Controller' . (empty($prefix) ? '' : DS . Inflector::camelize($prefix)));
           
$dir = new Folder($path[0]);
           
$controllers = $dir->find('.*Controller\.php');
        } else {
           
$path = App::path('Controller' . (empty($prefix) ? '' : DS . Inflector::camelize($prefix)), $plugin);
           
$dir = new Folder($path[0]);
           
$controllers = $dir->find('.*Controller\.php');
        }

        return
$controllers;
    }

   
/**
     * Check a node for existance, create it if it doesn't exist.
     *
     * @param string $path The path to check
     * @param string $alias The alias to create
     * @param int $parentId The parent id to use when creating.
     * @return array Aco Node array
     */
   
protected function _checkNode($path, $alias, $parentId = null)
    {
       
$node = $this->Aco->node($path);
        if (!
$node) {
           
$aliases = explode('/', $alias);
            foreach (
$aliases as $newAlias) {
               
$parentId = !empty($node) ? $node->id : $parentId;
               
$data = [
                   
'parent_id' => $parentId,
                   
'model' => null,
                   
'alias' => $newAlias,
                ];
               
$entity = $this->Aco->newEntity($data);
               
$node = $this->Aco->save($entity);
            }
           
$this->out(__d('cake_acl', 'Created Aco node: <success>{0}</success>', $path));
        } else {
           
$node = $node->first();
        }

        return
$node;
    }

   
/**
     * Get a list of registered callback methods
     *
     * @param string $className The class to reflect on.
     * @param string $pluginPath The plugin path.
     * @param string $prefixPath The prefix path.
     * @return array
     */
   
protected function _getCallbacks($className, $pluginPath = null, $prefixPath = null)
    {
       
$callbacks = [];
       
$namespace = $this->_getNamespace($className, $pluginPath, $prefixPath);
       
$reflection = new \ReflectionClass($namespace);
        if (
$reflection->isAbstract()) {
            return
$callbacks;
        }
        try {
           
$method = $reflection->getMethod('implementedEvents');
        } catch (
ReflectionException $e) {
            return
$callbacks;
        }
        if (
version_compare(phpversion(), '5.4', '>=')) {
           
$object = $reflection->newInstanceWithoutConstructor();
        } else {
           
$object = unserialize(
               
sprintf('O:%d:"%s":0:{}', strlen($className), $className)
            );
        }
       
$implementedEvents = $method->invoke($object);
        foreach (
$implementedEvents as $event => $callable) {
            if (
is_string($callable)) {
               
$callbacks[] = $callable;
            }
            if (
is_array($callable) && isset($callable['callable'])) {
               
$callbacks[] = $callable['callable'];
            }
        }

        return
$callbacks;
    }

   
/**
     * Check and Add/delete controller Methods
     *
     * @param string $className The classname to check
     * @param string $controllerName The controller name
     * @param array $node The node to check.
     * @param string $pluginPath The plugin path to use.
     * @param string $prefixPath The prefix path to use.
     * @return bool
     */
   
protected function _checkMethods($className, $controllerName, $node, $pluginPath = null, $prefixPath = null)
    {
       
$excludes = $this->_getCallbacks($className, $pluginPath, $prefixPath);
       
$baseMethods = get_class_methods(new Controller);
       
$namespace = $this->_getNamespace($className, $pluginPath, $prefixPath);
       
$methods = get_class_methods($namespace);
        if (
$methods == null) {
           
$this->err(__d('cake_acl', 'Unable to get methods for {0}', $className));

            return
false;
        }
       
$actions = array_diff($methods, $baseMethods);
       
$actions = array_diff($actions, $excludes);
        foreach (
$actions as $key => $action) {
            if (
strpos($action, '_', 0) === 0) {
                continue;
            }
           
$path = [
               
$this->rootNode,
               
$pluginPath,
               
$prefixPath,
               
$controllerName,
               
$action
           
];
           
$path = implode('/', Hash::filter($path));
           
$this->_checkNode($path, $action, $node->id);
           
$actions[$key] = $action;
        }
        if (
$this->_clean) {
           
$this->_cleaner($node->id, $actions);
        }

        return
true;
    }

   
/**
     * Recover an Acl Tree
     *
     * @return void
     */
   
public function recover()
    {
       
$type = Inflector::camelize($this->args[0]);
       
$this->Acl->{$type}->recover();
       
$this->out(__('Tree has been recovered, or tree did not need recovery.'));
    }

   
/**
     * Get the namespace for a given class.
     *
     * @param string $className The class you want a namespace for.
     * @param string $pluginPath The plugin path.
     * @param string $prefixPath The prefix path.
     * @return string
     */
   
protected function _getNamespace($className, $pluginPath = null, $prefixPath = null)
    {
       
$namespace = preg_replace('/(.*)Controller\//', '', $className);
       
$namespace = preg_replace('/\//', '\\', $namespace);
       
$namespace = preg_replace('/\.php/', '', $namespace);
       
$prefixPath = preg_replace('/\//', '\\', Inflector::camelize($prefixPath));
        if (!
$pluginPath) {
           
$rootNamespace = Configure::read('App.namespace');
        } else {
           
$rootNamespace = preg_replace('/\//', '\\', $pluginPath);
        }
       
$namespace = [
           
$rootNamespace,
           
'Controller',
           
$prefixPath,
           
$namespace
       
];

        return
implode('\\', Hash::filter($namespace));
    }

   
/**
     * Build prefixes for App and Plugins based on configured routes
     *
     * @return void
     */
   
protected function _buildPrefixes()
    {
       
$routes = Router::routes();
        foreach (
$routes as $key => $route) {
            if (isset(
$route->defaults['prefix'])) {
               
$prefixes = explode('/', $route->defaults['prefix']);
               
$prefix = implode('/', array_map(
                   
'Cake\\Utility\\Inflector::camelize',
                   
$prefixes
               
));
                if (!isset(
$route->defaults['plugin'])) {
                   
$this->prefixes[$prefix] = true;
                } else {
                   
$this->pluginPrefixes[$route->defaults['plugin']][$prefix] = true;
                }
            }
        }
    }

   
/**
     * Delete unused ACOs.
     *
     * @param int $parentId Id of the parent node.
     * @param array $preservedItems list of items that will not be erased.
     * @return void
     */
   
protected function _cleaner($parentId, $preservedItems = [])
    {
       
$nodes = $this->Aco->find()->where(['parent_id' => $parentId]);
       
$methodFlip = [];
        foreach (
$preservedItems as $preservedItem) {
           
$aliases = explode('/', $preservedItem);
            foreach (
$aliases as $alias) {
               
$methodFlip[$alias] = true;
            }
        }
        foreach (
$nodes as $node) {
            if (!isset(
$methodFlip[$node->alias])) {
               
$crumbs = $this->Aco->find('path', ['for' => $node->id, 'order' => 'lft']);
               
$path = null;
                foreach (
$crumbs as $crumb) {
                   
$path .= '/' . $crumb->alias;
                }
               
$entity = $this->Aco->get($node->id);
                if (
$this->Aco->delete($entity)) {
                   
$this->out(__d('cake_acl', 'Deleted Aco node: <warning>{0}</warning> and all children', $path));
                }
            }
        }
    }

   
/**
     * Get discovered app route prefixes
     *
     * @return array
     */
   
public function getPrefixes()
    {
        return
$this->prefixes;
    }

   
/**
     * Get discovered plugin route prefixes
     *
     * @return array
     */
   
public function getPluginPrefixes()
    {
        return
$this->pluginPrefixes;
    }

   
/**
     * Get the attached controller.
     *
     * @return \Cake\Controller\Controller
     */
   
public function getController()
    {
        return
$this->controller;
    }

   
/**
     * Get the attached shell.
     *
     * @return \Cake\Console\Shell
     */
   
public function getShell()
    {
        return
$this->Shell;
    }

   
/**
     * Attach a shell for output.
     *
     * @param \Cake\Console\Shell $shell Shell to attach
     * @return void
     */
   
public function setShell(Shell $shell)
    {
       
$this->Shell = $shell;
    }
}