Seditio Source
Root |
./othercms/croogo-4.0.7/vendor/friendsofcake/crud/src/Controller/Component/CrudComponent.php
<?php
namespace Crud\Controller\Component;

use
Cake\Controller\Component;
use
Cake\Controller\ComponentRegistry;
use
Cake\Core\App;
use
Cake\Event\Event;
use
Cake\Http\Exception\BadRequestException;
use
Cake\Http\Exception\MethodNotAllowedException;
use
Cake\Http\Exception\NotFoundException;
use
Cake\Http\Response;
use
Cake\Utility\Inflector;
use
Crud\Error\Exception\ActionNotConfiguredException;
use
Crud\Error\Exception\ListenerNotConfiguredException;
use
Crud\Error\Exception\MissingActionException;
use
Crud\Error\Exception\MissingListenerException;
use
Crud\Event\Subject;
use
Exception;

/**
 * Crud component
 *
 * Scaffolding on steroids! :)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 */
class CrudComponent extends Component
{

   
/**
     * The current controller action.
     *
     * @var string
     */
   
protected $_action;

   
/**
     * Reference to the current controller.
     *
     * @var \Cake\Controller\Controller
     */
   
protected $_controller;

   
/**
     * Reference to the current request.
     *
     * @var \Cake\Http\ServerRequest
     */
   
protected $_request;

   
/**
     * A flat array of the events triggered.
     *
     * @var array
     */
   
protected $_eventLog = [];

   
/**
     * Reference to the current event manager.
     *
     * @var \Cake\Event\EventManager
     */
   
protected $_eventManager;

   
/**
     * Cached property for Controller::$modelClass. This is
     * the model name of the current model.
     *
     * @var string
     */
   
protected $_modelName;

   
/**
     * List of listener objects attached to Crud.
     *
     * @var array
     */
   
protected $_listenerInstances = [];

   
/**
     * List of crud actions.
     *
     * @var array
     */
   
protected $_actionInstances = [];

   
/**
     * Components settings.
     *
     * `eventPrefix` All emitted events will be prefixed with this property value.
     *
     * `actions` contains an array of controller methods this component should offer implementation for.
     * Each action maps to a CrudAction class. `$controllerAction => $crudActionClass`.
     * Example: `array('admin_index' => 'Crud.Index')`
     * By default no actions are enabled.
     *
     * `listeners` List of internal-name => ${plugin}.${class} listeners
     * that will be bound automatically in Crud. By default the related model event
     * are bound. Events will always assume to be in the Controller/Event folder.
     *
     * `eventLogging` boolean to determine whether the class should log triggered events.
     *
     * @var array
     */
   
protected $_defaultConfig = [
       
'actions' => [],
       
'eventPrefix' => 'Crud',
       
'listeners' => [],
       
'messages' => [
           
'domain' => 'crud',
           
'invalidId' => [
               
'code' => 400,
               
'class' => BadRequestException::class,
               
'text' => 'Invalid id',
            ],
           
'recordNotFound' => [
               
'code' => 404,
               
'class' => NotFoundException::class,
               
'text' => 'Not found',
            ],
           
'badRequestMethod' => [
               
'code' => 405,
               
'class' => MethodNotAllowedException::class,
               
'text' => 'Method not allowed. This action permits only {methods}',
            ],
        ],
       
'eventLogging' => false,
    ];

   
/**
     * Constructor
     *
     * @param \Cake\Controller\ComponentRegistry $collection A ComponentCollection this component
     *   can use to lazy load its components.
     * @param array $config Array of configuration settings.
     */
   
public function __construct(ComponentRegistry $collection, $config = [])
    {
       
$config += ['actions' => [], 'listeners' => []];
       
$config['actions'] = $this->normalizeArray($config['actions']);
       
$config['listeners'] = $this->normalizeArray($config['listeners']);

       
$this->_controller = $collection->getController();
       
$this->_eventManager = $this->_controller->getEventManager();

       
parent::__construct($collection, $config);
    }

   
/**
     * Normalize config array
     *
     * @param array $array List to normalize
     * @return array
     */
   
public function normalizeArray(array $array)
    {
       
$normal = [];

        foreach (
$array as $action => $config) {
            if (
is_string($config)) {
               
$config = ['className' => $config];
            }

            if (
is_int($action)) {
                list(,
$action) = pluginSplit($config['className']);
            }

           
$action = Inflector::variable($action);
           
$normal[$action] = $config;
        }

        return
$normal;
    }

   
/**
     * Add self to list of components capable of dispatching an action.
     *
     * @param array $config Configuration values for component.
     * @return void
     */
   
public function initialize(array $config)
    {
       
parent::initialize($config);

       
$this->_action = $this->_controller->request->getParam('action');
       
$this->_request = $this->_controller->request;

        if (!isset(
$this->_controller->dispatchComponents)) {
           
$this->_controller->dispatchComponents = [];
        }

       
$this->_controller->dispatchComponents['Crud'] = true;
    }

   
/**
     * Loads listeners
     *
     * @param \Cake\Event\Event $event Event instance
     * @return void
     * @throws \Exception
     */
   
public function beforeFilter(Event $event)
    {
       
$this->_loadListeners();
       
$this->trigger('beforeFilter');
    }

   
/**
     * Called after the Controller::beforeFilter() and before the controller action.
     *
     * @param \Cake\Event\Event $event Event instance
     * @return void
     * @throws \Exception
     */
   
public function startup(Event $event)
    {
       
$this->_loadListeners();
       
$this->trigger('startup');
    }

   
/**
     * Execute a Crud action
     *
     * @param string $controllerAction Override the controller action to execute as.
     * @param array $args List of arguments to pass to the CRUD action (Usually an ID to edit / delete).
     * @return \Cake\Http\Response
     * @throws Exception If an action is not mapped.
     */
   
public function execute($controllerAction = null, $args = [])
    {
       
$this->_loadListeners();

       
$this->_action = $controllerAction ?: $this->_action;

       
$action = $this->_action;
        if (empty(
$args)) {
           
$args = $this->_request->getParam('pass');
        }

        try {
           
$event = $this->trigger('beforeHandle', $this->getSubject(compact('args', 'action')));

           
$response = $this->action($event->getSubject()->action)->handle($event->getSubject()->args);
            if (
$response instanceof Response) {
                return
$response;
            }
        } catch (
Exception $e) {
            if (isset(
$e->response)) {
                return
$e->response;
            }

            throw
$e;
        }

       
$view = null;
       
$crudAction = $this->action($action);
        if (
method_exists($crudAction, 'view')) {
           
$view = $crudAction->view();
        }

        return
$this->_controller->response = $this->_controller->render($view);
    }

   
/**
     * Get a CrudAction object by action name.
     *
     * @param string|null $name The controller action name.
     * @return \Crud\Action\BaseAction
     * @throws \Crud\Error\Exception\ActionNotConfiguredException
     * @throws \Crud\Error\Exception\MissingActionException
     */
   
public function action($name = null)
    {
        if (empty(
$name)) {
           
$name = $this->_action;
        }

       
$name = Inflector::variable($name);

        return
$this->_loadAction($name);
    }

   
/**
     * Enable one or multiple CRUD actions.
     *
     * @param string|array $actions The action to enable.
     * @return void
     * @throws \Crud\Error\Exception\ActionNotConfiguredException
     * @throws \Crud\Error\Exception\MissingActionException
     */
   
public function enable($actions)
    {
        foreach ((array)
$actions as $action) {
           
$this->action($action)->enable();
        }
    }

   
/**
     * Disable one or multiple CRUD actions.
     *
     * @param string|array $actions The action to disable.
     * @return void
     * @throws \Crud\Error\Exception\ActionNotConfiguredException
     * @throws \Crud\Error\Exception\MissingActionException
     */
   
public function disable($actions)
    {
        foreach ((array)
$actions as $action) {
           
$this->action($action)->disable();
        }
    }

   
/**
     * Map the view file to use for a controller action.
     *
     * To map multiple action views in one go pass an array as first argument and no second argument.
     *
     * @param string|array $action Action or array of actions
     * @param string|null $view View name
     * @return void
     * @throws \Crud\Error\Exception\ActionNotConfiguredException
     * @throws \Crud\Error\Exception\MissingActionException
     */
   
public function view($action, $view = null)
    {
        if (
is_array($action)) {
            foreach (
$action as $realAction => $realView) {
               
$this->action($realAction)->view($realView);
            }

            return;
        }

       
$this->action($action)->view($view);
    }

   
/**
     * Change the viewVar name for one or multiple actions.
     *
     * To map multiple action viewVars in one go pass an array as first argument and no second argument.
     *
     * @param string|array $action Action or array of actions.
     * @param string|null $viewVar View var name.
     * @return void
     * @throws \Crud\Error\Exception\ActionNotConfiguredException
     * @throws \Crud\Error\Exception\MissingActionException
     */
   
public function viewVar($action, $viewVar = null)
    {
        if (
is_array($action)) {
            foreach (
$action as $realAction => $realViewVar) {
               
$this->action($realAction)->viewVar($realViewVar);
            }

            return;
        }

       
$this->action($action)->viewVar($viewVar);
    }

   
/**
     * Map a controller action to a Model::find($method).
     *
     * To map multiple findMethods in one go pass an array as first argument and no second argument.
     *
     * @param string|array $action Action or array of actions.
     * @param string|null $method Find method name
     * @return void
     * @throws \Crud\Error\Exception\ActionNotConfiguredException
     * @throws \Crud\Error\Exception\MissingActionException
     */
   
public function findMethod($action, $method = null)
    {
        if (
is_array($action)) {
            foreach (
$action as $realAction => $realMethod) {
               
$this->action($realAction)->findMethod($realMethod);
            }

            return;
        }

       
$this->action($action)->findMethod($method);
    }

   
/**
     * Map action to an internal request type.
     *
     * @param string $action The Controller action to provide an implementation for.
     * @param string|array $config Config array or class name like Crud.Index.
     * @param bool $enable Should the mapping be enabled right away?
     * @return void
     * @throws \Crud\Error\Exception\ActionNotConfiguredException
     * @throws \Crud\Error\Exception\MissingActionException
     */
   
public function mapAction($action, $config = [], $enable = true)
    {
        if (
is_string($config)) {
           
$config = ['className' => $config];
        }
       
$action = Inflector::variable($action);
       
$this->setConfig('actions.' . $action, $config);

        if (
$enable) {
           
$this->enable($action);
        }
    }

   
/**
     * Check if a CRUD action has been mapped (whether it will be handled by CRUD component)
     *
     * @param string|null $action If null, use the current action.
     * @return bool
     * @throws \Crud\Error\Exception\ActionNotConfiguredException
     * @throws \Crud\Error\Exception\MissingActionException
     */
   
public function isActionMapped($action = null)
    {
        if (empty(
$action)) {
           
$action = $this->_action;
        }

       
$action = Inflector::variable($action);
       
$test = $this->getConfig('actions.' . $action);
        if (empty(
$test)) {
            return
false;
        }

        return
$this->action($action)->getConfig('enabled');
    }

   
/**
     * Attaches an event listener function to the controller for Crud Events.
     *
     * @param string|array $events Name of the Crud Event you want to attach to controller.
     * @param callable $callback Callable method or closure to be executed on event.
     * @param array $options Used to set the `priority` and `passParams` flags to the listener.
     * @return void
     */
   
public function on($events, $callback, $options = [])
    {
        foreach ((array)
$events as $event) {
            if (!
strpos($event, '.')) {
               
$event = $this->_config['eventPrefix'] . '.' . $event;
            }

           
$this->_eventManager->on($event, $options, $callback);
        }
    }

   
/**
     * Get a single event class.
     *
     * @param string $name Listener
     * @return \Crud\Listener\BaseListener
     * @throws \Crud\Error\Exception\ListenerNotConfiguredException
     * @throws \Crud\Error\Exception\MissingListenerException
     */
   
public function listener($name)
    {
        return
$this->_loadListener($name);
    }

   
/**
     * Add a new listener to Crud
     *
     * This will not load or initialize the listener, only lazy-load it.
     *
     * If `$name` is provided but no `$class` argument, the className will
     * be derived from the `$name`.
     *
     * CakePHP Plugin.ClassName format for `$name` and `$class` is supported.
     *
     * @param string $name Name
     * @param string $className Normal CakePHP plugin-dot annotation supported.
     * @param array $config Any default settings for a listener.
     * @return void
     */
   
public function addListener($name, $className = null, $config = [])
    {
        if (
strpos($name, '.') !== false) {
            list(
$plugin, $name) = pluginSplit($name);
           
$className = $plugin . '.' . Inflector::camelize($name);
        }

       
$name = Inflector::variable($name);
       
$this->setConfig(sprintf('listeners.%s', $name), compact('className') + $config);
    }

   
/**
     * Remove a listener from Crud.
     *
     * This will also detach it from the EventManager if it's attached.
     *
     * @param string $name Name
     * @return false|void
     */
   
public function removeListener($name)
    {
       
$listeners = $this->getConfig('listeners');
        if (!
array_key_exists($name, $listeners)) {
            return
false;
        }

        if (isset(
$this->_listenerInstances[$name])) {
           
$this->_eventManager->off($this->_listenerInstances[$name]);
            unset(
$this->_listenerInstances[$name]);
        }

        unset(
$listeners[$name]);
       
$this->_config['listeners'] = $listeners;
    }

   
/**
     * Triggers a Crud event by creating a new subject and filling it with $data
     * if $data is an instance of CrudSubject it will be reused as the subject
     * object for this event.
     *
     * If Event listeners return a CakeResponse object, the this method will throw an
     * exception and fill a 'response' property on it with a reference to the response
     * object.
     *
     * @param string $eventName Event name
     * @param \Crud\Event\Subject|null $data Event data
     * @throws Exception if any event listener return a CakeResponse object.
     * @return \Cake\Event\Event
     */
   
public function trigger($eventName, Subject $data = null)
    {
       
$eventName = $this->_config['eventPrefix'] . '.' . $eventName;

       
$Subject = $data ?: $this->getSubject();
       
$Subject->addEvent($eventName);

        if (!empty(
$this->_config['eventLogging'])) {
           
$this->logEvent($eventName, $data);
        }

       
$Event = new Event($eventName, $Subject);
       
$this->_eventManager->dispatch($Event);

        if (
$Event->result instanceof Response) {
           
$Exception = new Exception();
           
$Exception->response = $Event->result;
            throw
$Exception;
        }

        return
$Event;
    }

   
/**
     * Add a log entry for the event.
     *
     * @param string $eventName Event name
     * @param array|\Crud\Event\Subject $data Event data
     * @return void
     */
   
public function logEvent($eventName, $data = [])
    {
       
$this->_eventLog[] = [$eventName, $data];
    }

   
/**
     * Set or get defaults for listeners and actions.
     *
     * @param string $type Can be anything, but 'listeners' or 'actions' is currently only used.
     * @param string|array $name The name of the $type - e.g. 'api', 'relatedModels'
     *  or an array ('api', 'relatedModels'). If $name is an array, the $config will be applied
     *  to each entry in the $name array.
     * @param mixed $config If NULL, the defaults is returned, else the defaults are changed.
     * @return mixed
     */
   
public function defaults($type, $name, $config = null)
    {
        if (
$config !== null) {
            if (!
is_array($name)) {
               
$name = [$name];
            }

            foreach (
$name as $realName) {
               
$this->setConfig(sprintf('%s.%s', $type, $realName), $config);
            }

            return
null;
        }

        return
$this->getConfig(sprintf('%s.%s', $type, $name));
    }

   
/**
     * Returns an array of triggered events.
     *
     * @return array
     */
   
public function eventLog()
    {
        return
$this->_eventLog;
    }

   
/**
     * Sets the model class to be used during the action execution.
     *
     * @param string $modelName The name of the model to load.
     * @return void
     */
   
public function useModel($modelName)
    {
       
$this->_controller->loadModel($modelName);
        list(,
$this->_modelName) = pluginSplit($modelName);
    }

   
/**
     * Returns controller's table instance.
     *
     * @return \Cake\ORM\Table
     */
   
public function table()
    {
        return
$this->_controller->{$this->_modelName};
    }

   
/**
     * Returns new entity
     *
     * @param array $data Data
     * @return \Cake\Datasource\EntityInterface
     */
   
public function entity(array $data = [])
    {
        return
$this->table()->newEntity($data);
    }

   
/**
     * Returns controller instance
     *
     * @return \Cake\Controller\Controller
     */
   
public function controller()
    {
        return
$this->_controller;
    }

   
/**
     * Create a CakeEvent subject with the required properties.
     *
     * @param array $additional Additional properties for the subject.
     * @return \Crud\Event\Subject
     */
   
public function getSubject($additional = [])
    {
       
$subject = new Subject();
       
$subject->set($additional);

        return
$subject;
    }

   
/**
     * Load all event classes attached to Crud.
     *
     * @return void
     * @throws \Crud\Error\Exception\ListenerNotConfiguredException
     * @throws \Crud\Error\Exception\MissingListenerException
     */
   
protected function _loadListeners()
    {
        foreach (
array_keys($this->getConfig('listeners')) as $name) {
           
$this->_loadListener($name);
        }
    }

   
/**
     * Load a single event class attached to Crud.
     *
     * @param string $name Name
     * @return \Crud\Listener\BaseListener
     * @throws \Crud\Error\Exception\ListenerNotConfiguredException
     * @throws \Crud\Error\Exception\MissingListenerException
     */
   
protected function _loadListener($name)
    {
        if (!isset(
$this->_listenerInstances[$name])) {
           
$config = $this->getConfig('listeners.' . $name);

            if (empty(
$config)) {
                throw new
ListenerNotConfiguredException(sprintf('Listener "%s" is not configured', $name));
            }

           
$className = App::className($config['className'], 'Listener', 'Listener');
            if (empty(
$className)) {
                throw new
MissingListenerException('Could not find listener class: ' . $config['className']);
            }

           
$this->_listenerInstances[$name] = new $className($this->_controller);
            unset(
$config['className']);
           
$this->_listenerInstances[$name]->setConfig($config);

           
$this->_eventManager->on($this->_listenerInstances[$name]);

            if (
is_callable([$this->_listenerInstances[$name], 'setup'])) {
               
$this->_listenerInstances[$name]->setup();
            }
        }

        return
$this->_listenerInstances[$name];
    }

   
/**
     * Load a CrudAction instance.
     *
     * @param string $name The controller action name.
     * @return \Crud\Action\BaseAction
     * @throws \Crud\Error\Exception\ActionNotConfiguredException
     * @throws \Crud\Error\Exception\MissingActionException
     */
   
protected function _loadAction($name)
    {
        if (!isset(
$this->_actionInstances[$name])) {
           
$config = $this->getConfig('actions.' . $name);

            if (empty(
$config)) {
                throw new
ActionNotConfiguredException(sprintf('Action "%s" has not been mapped', $name));
            }

           
$className = App::className($config['className'], 'Action', 'Action');
            if (empty(
$className)) {
                throw new
MissingActionException('Could not find action class: ' . $config['className']);
            }

           
$this->_actionInstances[$name] = new $className($this->_controller);
            unset(
$config['className']);
           
$this->_actionInstances[$name]->setConfig($config);
        }

        return
$this->_actionInstances[$name];
    }
}