Convert Crud to submodule

This commit is contained in:
Andrew Bauer 2015-06-11 11:56:39 -05:00
parent dd9603f70f
commit d915d859ed
67 changed files with 4 additions and 18685 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "web/api/app/Plugin/Crud"]
path = web/api/app/Plugin/Crud
url = https://github.com/FriendsOfCake/crud.git

@ -0,0 +1 @@
Subproject commit f1cf70ffff0657db29388eca94e6c5ea4944a164

View File

@ -1,16 +0,0 @@
; This file is for unifying the coding style for different editors and IDEs.
; More information at http://editorconfig.org
root = false
[*]
indent_style = tab
indent_size = 2
charset = "utf-8"
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.yml]
indent_style = space
indent_size = 2

View File

@ -1,5 +0,0 @@
---
:major: 3
:minor: 0
:patch: 10
:special: ''

View File

@ -1,38 +0,0 @@
language: php
php:
- 5.3
- 5.4
- 5.5
env:
global:
- PLUGIN_NAME=Crud
- REQUIRE="cakephp/debug_kit:2.2.* cakedc/search:dev-develop"
- DB=mysql CAKE_VERSION=2.4
matrix:
- DB=mysql CAKE_VERSION=2.4
- DB=mysql CAKE_VERSION=2.5
matrix:
include:
- php: 5.4
env:
- COVERALLS=1
- php: 5.4
env:
- PHPCS=1 PHPCS_IGNORE="*/Test/*"
before_script:
- git clone https://github.com/FriendsOfCake/travis.git --depth 1 ../travis
- ../travis/before_script.sh
script:
- ../travis/script.sh
after_success:
- ../travis/after_success.sh
notifications:
email: false

View File

@ -1,69 +0,0 @@
# How to contribute
CakePHP-crud loves to welcome your contributions. There are several ways to help out:
* Create a ticket in GitHub, if you have found a bug
* Write testcases for open bug tickets
* Write patches for open bug/feature tickets, preferably with testcases included
* Contribute to the [documentation](https://github.com/jippi/cakephp-crud/tree/gh-pages)
There are a few guidelines that we need contributors to follow so that we have a
chance of keeping on top of things.
## Getting Started
* Make sure you have a [GitHub account](https://github.com/signup/free)
* Submit a ticket for your issue, assuming one does not already exist.
* Clearly describe the issue including steps to reproduce when it is a bug.
* Make sure you fill in the earliest version that you know has the issue.
* Fork the repository on GitHub.
## Making Changes
* Create a topic branch from where you want to base your work.
* This is usually the develop branch
* To quickly create a topic branch based on master; `git branch
master/my_contribution master` then checkout the new branch with `git
checkout master/my_contribution`. Better avoid working directly on the
`master` branch, to avoid conflicts if you pull in updates from origin.
* Make commits of logical units.
* Check for unnecessary whitespace with `git diff --check` before committing.
* Use descriptive commit messages and reference the #ticket number
* Core testcases should continue to pass. You can run tests locally or enable
[travis-ci](https://travis-ci.org/) for your fork, so all tests and codesniffs
will be executed.
* Your work should apply the CakePHP coding standards.
## Which branch to base the work
* Bugfix branches will be based on develop branch.
* New features that are backwards compatible will be based on develop branch
* New features or other non-BC changes will go in the next major release branch.
## Submitting Changes
* Push your changes to a topic branch in your fork of the repository.
* Submit a pull request to the repository with the correct target branch.
## Testcases and codesniffer
CakePHP-crud tests requires [PHPUnit](http://www.phpunit.de/manual/current/en/installation.html)
3.5 or higher. To run the testcases locally use the following command:
./lib/Cake/Console/cake test Crud AllCrud
To run the sniffs for CakePHP coding standards
phpcs -p --extensions=php --standard=CakePHP ./app/Plugin/Crud
Check the [cakephp-codesniffer](https://github.com/cakephp/cakephp-codesniffer)
repository to setup the CakePHP standard. The README contains installation info
for the sniff and phpcs.
# Additional Resources
* [CakePHP coding standards](http://book.cakephp.org/2.0/en/contributing/cakephp-coding-conventions.html)
* [Bug tracker](https://github.com/jippi/cakephp-crud/issues)
* [General GitHub documentation](https://help.github.com/)
* [GitHub pull request documentation](https://help.github.com/send-pull-requests/)
* #cakephp IRC channel on freenode.org

View File

@ -1,310 +0,0 @@
<?php
App::uses('AppShell', 'Console/Command');
App::uses('CrudSubject', 'Crud.Controller/Crud');
App::uses('TranslationsListener', 'Crud.Controller/Crud/Listener');
/**
* TranslationsShell
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*
*/
class TranslationsShell extends AppShell {
/**
* The array of raw stings to be written to the output file
*
* @var array
*/
public $lines = array();
/**
* The path to write the output file to
*
* @var string
*/
protected $_path = '';
/**
* Gets the option parser instance and configures it.
* By overriding this method you can configure the ConsoleOptionParser before returning it.
*
* @return ConsoleOptionParser
* @link http://book.cakephp.org/2.0/en/console-and-shells.html#Shell::getOptionParser
*/
public function getOptionParser() {
$parser = parent::getOptionParser();
return $parser
->addSubCommand('generate', array(
'help' => 'Generate the translation strings for CRUD component usage'
));
}
/**
* Create or update the file containing the translation strings for CRUD component usage
*
* @return void
*/
public function generate() {
$controllers = $this->_getControllers($this->args);
if (!$controllers) {
$this->out('<warning>No controllers found to be processed</warning>');
return;
}
$this->hr();
$this->out(sprintf('Processing translation strings for controllers: %s.', implode($controllers, ', ')));
$this->out('');
$path = $this->path();
if (file_exists($path)) {
$this->lines = array_map('rtrim', file($path));
} else {
$this->lines[] = '<?php';
}
foreach ($controllers as $name) {
$this->_processController($name);
}
return $this->_writeFile();
}
/**
* Add a doc block to the lines property with the passed message appropriately formatted
* If the doc block already exists - return false
*
* @param string $message
* @return boolean Success
*/
protected function _addDocBlock($message) {
$message = " * $message";
if (in_array($message, $this->lines)) {
return false;
}
$this->lines[] = '';
$this->lines[] = '/**';
$this->lines[] = $message;
$this->lines[] = ' */';
return true;
}
/**
* If no arguments are passed to the cli call, return all App controllers
* Otherwise, assume the arguments are a list of file paths to plugin model dirs or an individual plugin model
*
* @param array $args File paths to controllers to process
* @return array
*/
protected function _getControllers($args = array()) {
$objectType = 'Controller';
$controllers = array();
if ($args) {
foreach ($args as $arg) {
$plugin = $controller = null;
preg_match('@Plugin/([^/]+)@', $arg, $match);
if ($match) {
$plugin = $match[1];
}
preg_match('@Controller/([^/]+)@', $arg, $match);
if ($match) {
$controller = $match[1];
}
if (!$plugin && !$controller) {
$this->out("<info>Skipping argument:</info> $arg", 1, Shell::VERBOSE);
continue;
}
if ($plugin) {
if ($controller) {
$controllers[] = $plugin . '.' . $controller;
} else {
$pluginControllers = App::objects("$plugin.Controller");
foreach ($pluginControllers as &$c) {
$c = "$plugin.$c";
}
$controllers = array_merge($controllers, $pluginControllers);
}
} else {
$controllers[] = $controller;
}
}
} else {
$controllers = App::objects('Controller');
}
foreach ($controllers as $i => &$controller) {
$controller = preg_replace('/Controller(\.php)?$/', '', $controller);
if (preg_match('/^(?:(\w+)\.\1)?App$/', $controller)) {
unset($controllers[$i]);
}
}
return array_values($controllers);
}
/**
* Set or retrieve the path to write the output file to
* Defaults to APP/Config/i18n_crud.php
*
* @param string $path
* @return string
*/
public function path($path = null) {
if ($path) {
$this->_path = $path;
} elseif (!$this->_path) {
$this->_path = APP . 'Config' . DS . 'i18n_crud.php';
}
return $this->_path;
}
/**
* Get controller instance
*
* @param string $name Controller name
* @param string $plugin Plugin name
* @return Controller
* @codeCoverageIgnore
*/
protected function _loadController($name, $plugin) {
$className = $name . 'Controller';
$prefix = rtrim($plugin, '.');
App::uses($className, $plugin . 'Controller');
if (!class_exists($className)) {
$this->out("<info>Skipping:</info> $className, class could not be loaded", 1, Shell::VERBOSE);
return;
}
$request = new CakeRequest();
$Controller = new $className($request);
$Controller->constructClasses();
$Controller->startupProcess();
if (!$Controller->uses) {
$this->out("<info>Skipping:</info> $className, doesn't use any models", 1, Shell::VERBOSE);
return;
}
if (!isset($Controller->Crud)) {
$this->out("<info>Skipping:</info> $className, doesn't use Crud component", 1, Shell::VERBOSE);
return;
}
return $Controller;
}
/**
* For the given controller name, initialize the crud component and process each action.
* Create a listener for the setFlash event to log the flash message details.
*
* @param string $name Controller name
*/
protected function _processController($name) {
list($plugin, $name) = pluginSplit($name, true);
$prefix = rtrim($plugin, '.');
$Controller = $this->_loadController($name, $plugin);
if (!$Controller) {
return;
}
$this->_addDocBlock("$name CRUD Component translations");
$actions = array_keys($Controller->Crud->config('actions'));
foreach ($actions as $actionName) {
$this->_processAction($actionName, $Controller);
}
}
/**
* Process a single crud action. Initialize the action object, and trigger each
* flash message.
*
* @param string $actionName crud action name
* @param Controller $Controller instance
*/
protected function _processAction($actionName, $Controller) {
try {
$Controller->Crud->action($actionName);
$Controller->Crud->trigger('beforeHandle');
} catch(Exception $e) {
return;
}
$action = $Controller->Crud->action($actionName);
$messages = (array)$Controller->Crud->config('messages') + (array)$action->config('messages');
if (!$messages) {
return;
}
foreach (array_keys($messages) as $type) {
if ($type === 'domain') {
continue;
}
$message = $action->message($type);
$this->_processMessage($message, $action, $Controller->Crud);
}
}
/**
* Generates translation statement string and adds to lines property
*
* @param mixed $message
* @param mixed $action
* @param mixed $crud
*/
protected function _processMessage($message, $action, $crud) {
$text = $message['params']['original'];
if (!$text) {
return;
}
$domain = $action->config('messages.domain');
if (!$domain) {
$domain = $crud->config('messages.domain') ?: 'crud';
}
$string = "__d('$domain', '$text');";
if (in_array($string, $this->lines)) {
$this->out('<info>Skipping:</info> ' . $text, 1, Shell::VERBOSE);
} else {
$this->out('<success>Adding:</success> ' . $text);
$this->lines[] = $string;
}
}
/**
* Take the lines property, populated by the generate method - and write it
* out to the output file path
*
* @return string the file path written to
*/
protected function _writeFile() {
$path = $this->path();
$lines = implode($this->lines, "\n") . "\n";
$file = new File($path, true, 0644);
$file->write($lines);
$this->out(str_replace('APP', '', $path) . ' updated');
$this->hr();
return $path;
}
}

View File

@ -1,845 +0,0 @@
<?php
App::uses('Component', 'Controller');
App::uses('CrudSubject', 'Crud.Controller/Crud');
/**
* 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 {
/**
* Reference to a Session component.
*
* @var array
*/
public $components = array('Session');
/**
* The current controller action.
*
* @var string
*/
protected $_action;
/**
* Reference to the current controller.
*
* @var Controller
*/
protected $_controller;
/**
* Reference to the current request.
*
* @var CakeRequest
*/
protected $_request;
/**
* A flat array of the events triggered.
*
* @var array
*/
protected $_eventLog = array();
/**
* Reference to the current event manager.
*
* @var CakeEventManager
*/
protected $_eventManager;
/**
* Cached property for Controller::$modelClass. This is
* the model name of the current model.
*
* @var string
*/
protected $_modelName;
/**
* Cached property for the current model instance. Instance
* of Controller::$modelClass.
*
* @var Model
*/
protected $_model;
/**
* List of listener objects attached to Crud.
*
* @var array
*/
protected $_listenerInstances = array();
/**
* List of crud actions.
*
* @var array
*/
protected $_actionInstances = array();
/**
* 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 models' events
* 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
*/
public $settings = array(
'actions' => array(),
'eventPrefix' => 'Crud',
'listeners' => array(
'RelatedModels' => 'Crud.RelatedModels'
),
'messages' => array(
'domain' => 'crud',
'invalidId' => array(
'code' => 400,
'class' => 'BadRequestException',
'text' => 'Invalid id'
),
'recordNotFound' => array(
'code' => 404,
'class' => 'NotFoundException',
'text' => 'Not found'
),
'badRequestMethod' => array(
'code' => 405,
'class' => 'MethodNotAllowedException',
'text' => 'Method not allowed. This action permits only {methods}'
)
),
'eventLogging' => false
);
/**
* Constructor
*
* @param ComponentCollection $collection A ComponentCollection this component can use to lazy load its components.
* @param array $settings Array of configuration settings.
* @return void
*/
public function __construct(ComponentCollection $collection, $settings = array()) {
parent::__construct($collection, $this->_mergeConfig($this->settings, $settings));
}
/**
* Make sure to update the list of known controller methods before startup is called.
*
* The reason for this is that if we don't, the Auth component won't execute any callbacks on the controller
* like isAuthorized.
*
* @param Controller $controller
* @return void
*/
public function initialize(Controller $controller) {
$this->_normalizeConfig();
$this->_controller = $controller;
$this->_controller->methods = array_keys(array_flip($this->_controller->methods) + array_flip(array_keys($this->settings['actions'])));
$this->_action = $this->_controller->request->action;
$this->_request = $this->_controller->request;
$this->_eventManager = $this->_controller->getEventManager();
if (!isset($this->_controller->dispatchComponents)) {
$this->_controller->dispatchComponents = array();
}
$name = str_replace('Component', '', get_class($this));
$this->_controller->dispatchComponents[$name] = true;
$this->_loadListeners();
$this->trigger('initialize');
}
/**
* Called after the Controller::beforeFilter() and before the controller action.
*
* @param Controller $controller Controller with components to startup.
* @return void
*/
public function startup(Controller $controller) {
$this->_loadListeners();
$this->trigger('startup');
}
/**
* Alias for `execute`.
*
* @deprecated Will be removed in Crud 3.1
* @param string $controllerAction Override the controller action to execute as.
* @param array $arguments List of arguments to pass to the CRUD action (Usually an ID to edit / delete).
* @return CakeResponse
* @throws CakeException If an action is not mapped.
*/
public function executeAction($controllerAction = null, $args = array()) {
return $this->execute($controllerAction, $args);
}
/**
* Execute a Crud action
*
* @param string $controllerAction Override the controller action to execute as.
* @param array $arguments List of arguments to pass to the CRUD action (Usually an ID to edit / delete).
* @return CakeResponse
* @throws CakeException If an action is not mapped.
*/
public function execute($controllerAction = null, $args = array()) {
$this->_loadListeners();
$this->_action = $controllerAction ?: $this->_action;
$action = $this->_action;
if (empty($args)) {
$args = $this->_request->params['pass'];
}
try {
$subject = $this->trigger('beforeHandle', compact('args', 'action'));
$response = $this->action($subject->action)->handle($subject);
if ($response instanceof CakeResponse) {
return $response;
}
} catch (Exception $e) {
if (isset($e->response)) {
return $e->response;
}
throw $e;
}
$view = $this->action($action)->view();
return $this->_controller->response = $this->_controller->render($view);
}
/**
* Get a CrudAction object by action name.
*
* @param string $name The controller action name.
* @return CrudAction
*/
public function action($name = null) {
if (empty($name)) {
$name = $this->_action;
}
return $this->_loadAction($name);
}
/**
* Enable one or multiple CRUD actions.
*
* @param string|array $actions The action to enable.
* @return void
*/
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
*/
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 the first argument with no second argument.
*
* @param string|array $action
* @param string $view
* @return void
*/
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 the first argument with no second argument.
*
* @param string|array $action
* @param string $viewVar
* @return void
*/
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 the first argument with no second argument.
*
* @param string|array $action
* @param string $method
* @return void
*/
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 $setting Settings array or one of the CRUD events (index, add, edit, delete, view).
* @param boolean $enable Should the mapping be enabled right away?
* @return void
*/
public function mapAction($action, $settings, $enable = true) {
$this->config('actions.' . $action, $settings);
$this->_normalizeConfig('actions');
if ($enable) {
$this->enable($action);
}
}
/**
* Check if a CRUD action has been mapped (whether it will be handled by CRUD component)
*
* @param string $action If null, use the current action.
* @return boolean
*/
public function isActionMapped($action = null) {
if (empty($action)) {
$action = $this->_action;
}
try {
$test = $this->config('actions.' . $action);
if (empty($test)) {
return false;
}
return $this->action($action)->config('enabled');
} catch (Exception $e) {
}
return false;
}
/**
* 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 callback $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 = array()) {
foreach ((array)$events as $event) {
if (!strpos($event, '.')) {
$event = $this->settings['eventPrefix'] . '.' . $event;
}
$this->_eventManager->attach($callback, $event, $options);
}
}
/**
* Get a single event class.
*
* @param string $name
* @return CrudBaseEvent
*/
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
* @param string $class Normal CakePHP plugin-dot annotation supported.
* @param array $defaults Any default settings for a listener.
* @return void
*/
public function addListener($name, $class = null, $defaults = array()) {
if (strpos($name, '.') !== false) {
list($plugin, $name) = pluginSplit($name);
$name = strtolower($name);
$class = $plugin . '.' . ucfirst($name);
}
$this->config(sprintf('listeners.%s', $name), array('className' => $class) + $defaults);
}
/**
* Remove a listener from Crud.
*
* This will also detach it from the EventManager if it's attached.
*
* @param string $name
* @return boolean
*/
public function removeListener($name) {
$listeners = $this->config('listeners');
if (!array_key_exists($name, $listeners)) {
return false;
}
if (isset($this->_listenerInstances[$name])) {
$this->_eventManager->detach($this->_listenerInstances[$name]);
unset($this->_listenerInstances[$name]);
}
unset($listeners[$name]);
$this->settings['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 this method will throw an
* exception and fill a 'response' property on it with a reference to the response
* object.
*
* @param string $eventName
* @param array $data
* @throws Exception if any event listener return a CakeResponse object.
* @return CrudSubject
*/
public function trigger($eventName, $data = array()) {
$eventName = $this->settings['eventPrefix'] . '.' . $eventName;
$subject = $data instanceof CrudSubject ? $data : $this->getSubject($data);
$subject->addEvent($eventName);
if (!empty($this->settings['eventLogging'])) {
$this->logEvent($eventName, $data);
}
$event = new CakeEvent($eventName, $subject);
$this->_eventManager->dispatch($event);
if ($event->result instanceof CakeResponse) {
$exception = new Exception();
$exception->response = $event->result;
throw $exception;
}
$subject->stopped = false;
if ($event->isStopped()) {
$subject->stopped = true;
}
return $subject;
}
/**
* Add a log entry for the event.
*
* @param string $eventName
* @param array $data
* @return void
*/
public function logEvent($eventName, $data = array()) {
$this->_eventLog[] = array(
$eventName,
$data
);
}
/**
* Sets a configuration variable into this component.
*
* If called with no arguments, all configuration values are
* returned.
*
* $key is interpreted with dot notation, like the one used for
* Configure::write().
*
* If $key is a string and $value is not passed, it will return the
* value associated with such key.
*
* If $key is an array and $value is empty, then $key will
* be interpreted as key => value dictionary of settings and
* it will be merged directly with $this->settings.
*
* If $key is a string, the value will be inserted in the specified
* slot as indicated using the dot notation.
*
* @param mixed $key
* @param mixed $value
* @return mixed|CrudComponent
*/
public function config($key = null, $value = null) {
if ($key === null && $value === null) {
return $this->settings;
}
if ($value === null) {
if (is_array($key)) {
$this->settings = Hash::merge($this->settings, $key);
return $this;
}
return Hash::get($this->settings, $key);
}
if (is_array($value)) {
$value = array_merge((array)Hash::get($this->settings, $key), $value);
}
$this->settings = Hash::insert($this->settings, $key, $value);
foreach (array('listeners', 'actions') as $type) {
if (strpos($key, $type . '.') === 0) {
$this->_normalizeConfig($type);
}
}
return $this;
}
/**
* Set or get defaults for listeners and actions.
*
* @param string $type Can be anything, but 'listeners' or 'actions' are only currently 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 are returned, else the defaults are changed.
* @return mixed
*/
public function defaults($type, $name, $config = null) {
if ($config !== null) {
if (!is_array($name)) {
$name = array($name);
}
foreach ($name as $realName) {
$this->config(sprintf('%s.%s', $type, $realName), $config);
}
return;
}
return $this->config(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(, $modelName) = pluginSplit($modelName);
$this->_model = $this->_controller->{$modelName};
$this->_modelName = $this->_model->name;
}
/**
* Create a CakeEvent subject with the required properties.
*
* @param array $additional Additional properties for the subject.
* @return CrudSubject
*/
public function getSubject($additional = array()) {
if (empty($this->_model) || empty($this->_modelName)) {
$this->_setModelProperties();
}
$subject = new CrudSubject();
$subject->crud = $this;
$subject->controller = $this->_controller;
$subject->model = $this->_model;
$subject->modelClass = $this->_modelName;
$subject->action = $this->_action;
$subject->request = $this->_request;
$subject->response = $this->_controller->response;
$subject->set($additional);
return $subject;
}
/**
* Return all vaidation errors.
*
* @return array
*/
public function validationErrors() {
$return = array();
$models = ClassRegistry::keys();
foreach ($models as $currentModel) {
$currentObject = ClassRegistry::getObject($currentModel);
if ($currentObject instanceof Model) {
$return[$currentObject->alias] = $currentObject->validationErrors;
}
}
return $return;
}
/**
* Normalize action configuration
*
* If an action doesn't have a CrudClass specified (the value part of the array)
* try to compute it by exploding on action name on '_' and take the last chunk
* as CrudClass identifier.
*
* @param mixed $types Class type(s).
* @return void
* @throws CakeException If className is missing for listener.
*/
protected function _normalizeConfig($types = null) {
if (!$types) {
$types = array('listeners', 'actions');
}
foreach ((array)$types as $type) {
$this->settings[$type] = Hash::normalize($this->settings[$type]);
foreach ($this->settings[$type] as $name => $settings) {
if (is_array($settings) && !empty($settings['className'])) {
$this->settings[$type][$name] = $settings;
continue;
}
$className = null;
if (empty($settings)) {
$settings = array();
} elseif (is_string($settings)) {
$className = $settings;
$settings = array();
}
if ($type === 'listeners' && strpos($name, '.') !== false) {
unset($this->settings[$type][$name]);
$settings['className'] = $name;
list($plugin, $name) = pluginSplit($name);
$name = Inflector::camelize($name);
}
$className = $this->_handlerClassName($name, $className);
if (empty($settings['className'])) {
$settings['className'] = $className;
}
$this->settings[$type][$name] = $settings;
}
}
}
/**
* Generate valid class name for action and listener handler.
*
* @param string $action
* @param string $className
* @return string Class name
*/
protected function _handlerClassName($action, $className) {
if (empty($className)) {
if (strstr($action, '_') !== false) {
list($prefix, $className) = explode('_', $action, 2);
$className = 'Crud.' . ucfirst($className);
} else {
$className = 'Crud.' . ucfirst($action);
}
} elseif (strpos($className, '.') === false) {
$className = 'Crud.' . ucfirst($className);
}
return ucfirst($className);
}
/**
* Load all event classes attached to Crud.
*
* @return void
*/
protected function _loadListeners() {
foreach (array_keys($this->config('listeners')) as $name) {
$this->_loadListener($name);
}
}
/**
* Load a single event class attached to Crud.
*
* @param string $name
* @return CrudListener
* @throws CakeException
*/
protected function _loadListener($name) {
if (!isset($this->_listenerInstances[$name])) {
$config = $this->config('listeners.' . $name);
if (empty($config)) {
throw new CakeException(sprintf('Listener "%s" is not configured', $name));
}
list($plugin, $class) = pluginSplit($config['className'], true);
$class .= 'Listener';
App::uses($class, $plugin . 'Controller/Crud/Listener');
$subject = $this->getSubject();
$this->_listenerInstances[$name] = new $class($subject, $config);
$this->_eventManager->attach($this->_listenerInstances[$name]);
if (is_callable(array($this->_listenerInstances[$name], 'setup'))) {
$this->_listenerInstances[$name]->setup();
}
}
return $this->_listenerInstances[$name];
}
/**
* Load a CrudAction instance.
*
* @param string $name The controller action name.
* @return CrudAction
* @throws CakeException If action is not mapped.
*/
protected function _loadAction($name) {
if (!isset($this->_actionInstances[$name])) {
$config = $this->config('actions.' . $name);
if (empty($config)) {
throw new CakeException(sprintf('Action "%s" has not been mapped', $name));
}
list($plugin, $class) = pluginSplit($config['className'], true);
$class = ucfirst($class);
if (in_array($class, array('Index', 'View', 'Add', 'Edit', 'Delete'))) {
if (!empty($plugin) && $plugin !== 'Crud.') {
throw new CakeException('The build-in CrudActions (Index, View, Add, Edit and Delete) must be loaded from the Crud plugin');
}
$plugin = 'Crud.';
}
$class .= 'CrudAction';
App::uses($class, $plugin . 'Controller/Crud/Action');
$subject = $this->getSubject(array('action' => $name));
$this->_actionInstances[$name] = new $class($subject, $config);
$this->_eventManager->attach($this->_actionInstances[$name]);
}
return $this->_actionInstances[$name];
}
/**
* Set internal model properties from the controller.
*
* @return void
* @throws CakeException If unable to get model instance.
*/
protected function _setModelProperties() {
$this->_modelName = $this->_controller->modelClass;
if (empty($this->_modelName)) {
$this->_model = null;
$this->_modelName = null;
return;
}
$this->_model = $this->_controller->{$this->_modelName};
if (empty($this->_model)) {
throw new CakeException('No model loaded in the Controller by the name "' . $this->_modelName . '". Please add it to $uses.');
}
}
/**
* Merge configuration arrays.
*
* Allow us to change e.g. a listener config without losing defaults.
*
* This is like merge_array_recursive - with the difference that
* duplicate keys aren't changed to an array with both values, but
* overridden.
*
* @param array $array1
* @param array $array2
* @return array
*/
protected function _mergeConfig(array $array1, array $array2) {
$merged = $array1;
foreach ($array2 as $key => $value) {
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
$merged[$key] = $this->_mergeConfig($merged[$key], $value);
continue;
}
$merged[$key] = $value;
}
return $merged;
}
}

View File

@ -1,131 +0,0 @@
<?php
App::uses('CrudAction', 'Crud.Controller/Crud');
/**
* Handles 'Add' Crud actions
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class AddCrudAction extends CrudAction {
/**
* Default settings for 'add' actions
*
* `enabled` Is this crud action enabled or disabled
*
* `view` A map of the controller action and the view to render
* If `NULL` (the default) the controller action name will be used
*
* `relatedModels` is a map of the controller action and the whether it should fetch associations lists
* to be used in select boxes. An array as value means it is enabled and represent the list
* of model associations to be fetched
*
* `saveOptions` Raw array passed as 2nd argument to saveAll() in `add` and `edit` method
* If you configure a key with your action name, it will override the default settings.
* This is useful for adding fieldList to enhance security in saveAll.
*
* @var array
*/
protected $_settings = array(
'enabled' => true,
'saveMethod' => 'saveAssociated',
'view' => null,
'relatedModels' => true,
'saveOptions' => array(
'validate' => 'first',
'atomic' => true
),
'api' => array(
'methods' => array('put', 'post'),
'success' => array(
'code' => 201,
'data' => array(
'subject' => array('id')
)
),
'error' => array(
'exception' => array(
'type' => 'validate',
'class' => 'CrudValidationException'
)
)
),
'redirect' => array(
'post_add' => array(
'reader' => 'request.data',
'key' => '_add',
'url' => array('action' => 'add')
),
'post_edit' => array(
'reader' => 'request.data',
'key' => '_edit',
'url' => array('action' => 'edit', array('subject.key', 'id'))
)
),
'messages' => array(
'success' => array(
'text' => 'Successfully created {name}'
),
'error' => array(
'text' => 'Could not create {name}'
)
),
'serialize' => array()
);
/**
* Constant representing the scope of this action
*
* @var integer
*/
const ACTION_SCOPE = CrudAction::SCOPE_MODEL;
/**
* HTTP GET handler
*
* @return void
*/
protected function _get() {
$request = $this->_request();
$model = $this->_model();
$model->create();
$request->data = $model->data;
$this->_trigger('beforeRender', array('success' => false));
}
/**
* HTTP POST handler
*
* @return void
*/
protected function _post() {
$request = $this->_request();
$model = $this->_model();
$this->_trigger('beforeSave');
if (call_user_func(array($model, $this->saveMethod()), $request->data, $this->saveOptions())) {
$this->setFlash('success');
$subject = $this->_trigger('afterSave', array('success' => true, 'created' => true, 'id' => $model->id));
return $this->_redirect($subject, array('action' => 'index'));
}
$this->setFlash('error');
$subject = $this->_trigger('afterSave', array('success' => false, 'created' => false));
$request->data = Hash::merge($request->data, $model->data);
$this->_trigger('beforeRender', $subject);
}
/**
* HTTP PUT handler
*
* @return void
*/
protected function _put() {
return $this->_post();
}
}

View File

@ -1,108 +0,0 @@
<?php
App::uses('CrudAction', 'Crud.Controller/Crud');
/**
* Handles 'Delete' Crud actions
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class DeleteCrudAction extends CrudAction {
/**
* Default settings for 'add' actions
*
* `enabled` Is this crud action enabled or disabled
*
* `findMethod` The default `Model::find()` method for reading data
*
* @var array
*/
protected $_settings = array(
'enabled' => true,
'findMethod' => 'count',
'messages' => array(
'success' => array(
'text' => 'Successfully deleted {name}'
),
'error' => array(
'text' => 'Could not delete {name}'
)
),
'api' => array(
'success' => array(
'code' => 200
),
'error' => array(
'code' => 400
)
)
);
/**
* Constant representing the scope of this action
*
* @var integer
*/
const ACTION_SCOPE = CrudAction::SCOPE_RECORD;
/**
* HTTP DELETE handler
*
* @throws NotFoundException If record not found
* @param string $id
* @return void
*/
protected function _delete($id = null) {
if (!$this->_validateId($id)) {
return false;
}
$request = $this->_request();
$model = $this->_model();
$query = array();
$query['conditions'] = array($model->escapeField() => $id);
$findMethod = $this->_getFindMethod('count');
$subject = $this->_trigger('beforeFind', compact('id', 'query', 'findMethod'));
$query = $subject->query;
$count = $model->find($subject->findMethod, $query);
if (empty($count)) {
$this->_trigger('recordNotFound', compact('id'));
$message = $this->message('recordNotFound', array('id' => $id));
$exceptionClass = $message['class'];
throw new $exceptionClass($message['text'], $message['code']);
}
$subject = $this->_trigger('beforeDelete', compact('id'));
if ($subject->stopped) {
$this->setFlash('error');
return $this->_redirect($subject, array('action' => 'index'));
}
if ($model->delete($id)) {
$this->setFlash('success');
$subject = $this->_trigger('afterDelete', array('id' => $id, 'success' => true));
} else {
$this->setFlash('error');
$subject = $this->_trigger('afterDelete', array('id' => $id, 'success' => false));
}
$this->_redirect($subject, array('action' => 'index'));
}
/**
* HTTP POST handler
*
* @param mixed $id
* @return void
*/
protected function _post($id = null) {
return $this->_delete($id);
}
}

View File

@ -1,293 +0,0 @@
<?php
App::uses('CrudAction', 'Crud.Controller/Crud');
/**
* Handles 'Edit' Crud actions
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class EditCrudAction extends CrudAction {
/**
* Default settings for 'edit' actions
*
* `enabled` Is this crud action enabled or disabled
*
* `findMethod` The default `Model::find()` method for reading data
*
* `view` A map of the controller action and the view to render
* If `NULL` (the default) the controller action name will be used
*
* `relatedModels` is a map of the controller action and the whether it should fetch associations lists
* to be used in select boxes. An array as value means it is enabled and represent the list
* of model associations to be fetched
*
* `validateId` ID Argument validation - by default it will inspect your model's primary key
* and based on its data type either use integer or UUID validation.
* Can be disabled by setting it to "false". Supports "integer" and "uuid" configuration
* By default its configuration is NULL, which means "auto detect"
*
* `saveOptions` Raw array passed as 2nd argument to saveAll() in `add` and `edit` method
* If you configure a key with your action name, it will override the default settings.
* This is useful for adding fieldList to enhance security in saveAll.
*
* @var array
*/
protected $_settings = array(
'enabled' => true,
'findMethod' => 'first',
'saveMethod' => 'saveAssociated',
'view' => null,
'relatedModels' => true,
'validateId' => null,
'saveOptions' => array(
'validate' => 'first',
'atomic' => true
),
'messages' => array(
'success' => array(
'text' => 'Successfully updated {name}'
),
'error' => array(
'text' => 'Could not update {name}'
)
),
'redirect' => array(
'post_add' => array(
'reader' => 'request.data',
'key' => '_add',
'url' => array('action' => 'add')
),
'post_edit' => array(
'reader' => 'request.data',
'key' => '_edit',
'url' => array('action' => 'edit', array('subject.key', 'id'))
)
),
'api' => array(
'methods' => array('put', 'post'),
'success' => array(
'code' => 200
),
'error' => array(
'exception' => array(
'type' => 'validate',
'class' => 'CrudValidationException'
)
)
),
'serialize' => array()
);
/**
* Constant representing the scope of this action
*
* @var integer
*/
const ACTION_SCOPE = CrudAction::SCOPE_RECORD;
/**
* HTTP GET handler
*
* @throws NotFoundException If record not found
* @param string $id
* @return void
*/
protected function _get($id = null) {
if (!$this->_validateId($id)) {
return false;
}
$request = $this->_request();
$model = $this->_model();
$request->data = $this->_findRecord($id);
if (empty($request->data)) {
return $this->_notFound($id);
}
$item = $request->data;
$subject = $this->_trigger('afterFind', compact('id', 'item'));
$request->data = Hash::merge($request->data, $model->data, $subject->item);
$this->_trigger('beforeRender');
}
/**
* HTTP PUT handler
*
* @param mixed $id
* @return void
*/
protected function _put($id = null) {
if (!$this->_validateId($id)) {
return false;
}
$request = $this->_request();
$model = $this->_model();
$existing = $this->_findRecord($id, 'count');
if (empty($existing)) {
return $this->_notFound($id);
}
$request->data = $this->_injectPrimaryKey($request->data, $id, $model);
$this->_trigger('beforeSave', compact('id'));
if (call_user_func(array($model, $this->saveMethod()), $request->data, $this->saveOptions())) {
$this->setFlash('success');
$subject = $this->_trigger('afterSave', array('id' => $id, 'success' => true, 'created' => false));
return $this->_redirect($subject, array('action' => 'index'));
}
$this->setFlash('error');
$subject = $this->_trigger('afterSave', array('id' => $id, 'success' => false, 'created' => false));
$this->_trigger('beforeRender', $subject);
}
/**
* Find a record from the ID
*
* @param string $id
* @param string $findMethod
* @return array
*/
protected function _findRecord($id, $findMethod = null) {
$model = $this->_model();
$query = array();
$query['conditions'] = array($model->escapeField() => $id);
if (!$findMethod) {
$findMethod = $this->_getFindMethod($findMethod);
}
$subject = $this->_trigger('beforeFind', compact('query', 'findMethod'));
return $model->find($subject->findMethod, $subject->query);
}
/**
* Throw exception if a record is not found
*
* @throws Exception
* @param string $id
* @return void
*/
protected function _notFound($id) {
$this->_trigger('recordNotFound', compact('id'));
$message = $this->message('recordNotFound', compact('id'));
$exceptionClass = $message['class'];
throw new $exceptionClass($message['text'], $message['code']);
}
/**
* HTTP POST handler
*
* Thin proxy for _put
*
* @param mixed $id
* @return void
*/
protected function _post($id = null) {
return $this->_put($id);
}
/**
* Inject the id (from the URL) into the data to be saved.
*
* Determine what the format of the data is there are two formats accepted by cake:
*
* array(
* 'Model' => array('stuff' => 'here')
* );
*
* and
*
* array('stuff' => 'here')
*
* The latter is most appropriate for API calls.
*
* If either the first array key is Capitalized, or the model alias is present in the form data,
* The id will be injected under the model-alias key:
*
* array(
* 'Model' => array('stuff' => 'here', 'id' => $id)
* );
*
* // HABTM example
* array(
* 'Category' => array('Category' => array(123)),
* 'Model' => array('id' => $id) // <- added
* );
*
* If the model-alias key is absent AND the first array key is not capitalized, inject in the root:
*
* array('stuff' => 'here', 'id' => $id)
*
*
* @param array $data
* @param mixed $id
* @param Model $model
* @return array
*/
protected function _injectPrimaryKey($data, $id, $model) {
$key = key($data);
$keyIsModelAlias = (strtoupper($key[0]) === $key[0]);
if (isset($data[$model->alias]) || $keyIsModelAlias) {
$data[$model->alias][$model->primaryKey] = $id;
} else {
$data[$model->primaryKey] = $id;
}
return $data;
}
/**
* Is the passed ID valid?
*
* Validate the id in the URL (the parent function) and then validate the id in the data.
*
* The data-id check is independent of the config setting `validateId`; this checks whether
* the id in the URL matches the id in the submitted data (a type insensitive check). If
* the id is different, this probably indicates a malicious form submission, attempting
* to add/edit a record the user doesn't have permission for by submitting to a URL they
* do have permission to access
*
* @param mixed $id
* @return boolean
* @throws BadRequestException If id is invalid
*/
protected function _validateId($id) {
parent::_validateId($id);
$request = $this->_request();
if (!$request->data) {
return true;
}
$dataId = null;
$model = $this->_model();
$dataId = $request->data($model->alias . '.' . $model->primaryKey) ?: $request->data($model->primaryKey);
if ($dataId === null) {
return true;
}
// deliberately type insensitive
if ($dataId == $id) {
return true;
}
$this->_trigger('invalidId', array('id' => $dataId));
$message = $this->message('invalidId');
$exceptionClass = $message['class'];
throw new $exceptionClass($message['text'], $message['code']);
}
}

View File

@ -1,120 +0,0 @@
<?php
App::uses('CrudAction', 'Crud.Controller/Crud');
/**
* Handles 'Index' Crud actions
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class IndexCrudAction extends CrudAction {
/**
* Default settings for 'index' actions
*
* `enabled` Is this crud action enabled or disabled
*
* `findMethod` The default `Model::find()` method for reading data
*
* `view` A map of the controller action and the view to render
* If `NULL` (the default) the controller action name will be used
*
* @var array
*/
protected $_settings = array(
'enabled' => true,
'findMethod' => 'all',
'view' => null,
'viewVar' => null,
'serialize' => array(),
'api' => array(
'success' => array(
'code' => 200
),
'error' => array(
'code' => 400
)
)
);
/**
* Constant representing the scope of this action
*
* @var integer
*/
const ACTION_SCOPE = CrudAction::SCOPE_MODEL;
/**
* Change the name of the view variable name
* of the data when its sent to the view
*
* @param mixed $name
* @return mixed
*/
public function viewVar($name = null) {
if (empty($name)) {
return $this->config('viewVar') ?: Inflector::variable($this->_controller()->name);
}
return $this->config('viewVar', $name);
}
/**
* Compute pagination settings
*
* Initializes PaginatorComponent if it isn't loaded already
* Modified the findType based on the CrudAction configuration
*
* @return array The Paginator settings
*/
public function paginationConfig() {
$controller = $this->_controller();
if (!isset($controller->Paginator)) {
$pagination = isset($controller->paginate) ? $controller->paginate : array();
$controller->Paginator = $controller->Components->load('Paginator', $pagination);
}
$Paginator = $controller->Paginator;
$settings = &$Paginator->settings;
if (isset($settings[$controller->modelClass])) {
if (empty($settings[$controller->modelClass]['findType'])) {
$settings[$controller->modelClass]['findType'] = $this->_getFindMethod('all');
}
} elseif (empty($settings['findType'])) {
$settings['findType'] = $this->_getFindMethod('all');
}
return $settings;
}
/**
* HTTP GET handler
*
* @return void
*/
protected function _get() {
$this->paginationConfig();
$controller = $this->_controller();
$success = true;
$viewVar = $this->viewVar();
$subject = $this->_trigger('beforePaginate', array('paginator' => $controller->Paginator, 'success' => $success, 'viewVar' => $viewVar));
$items = $controller->paginate($this->_model());
$subject = $this->_trigger('afterPaginate', array('success' => $subject->success, 'viewVar' => $subject->viewVar, 'items' => $items));
$items = $subject->items;
if ($items instanceof Iterator) {
$items = iterator_to_array($items);
}
$controller->set(array('success' => $subject->success, $subject->viewVar => $items));
$this->_trigger('beforeRender', $subject);
}
}

View File

@ -1,94 +0,0 @@
<?php
App::uses('CrudAction', 'Crud.Controller/Crud');
/**
* Handles 'View' Crud actions
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class ViewCrudAction extends CrudAction {
/**
* Default settings for 'view' actions
*
* `enabled` Is this crud action enabled or disabled
*
* `findMethod` The default `Model::find()` method for reading data
*
* `view` A map of the controller action and the view to render
* If `NULL` (the default) the controller action name will be used
*
* @var array
*/
protected $_settings = array(
'enabled' => true,
'findMethod' => 'first',
'view' => null,
'viewVar' => null,
'serialize' => array()
);
/**
* Constant representing the scope of this action
*
* @var integer
*/
const ACTION_SCOPE = CrudAction::SCOPE_RECORD;
/**
* Change the name of the view variable name
* of the data when its sent to the view
*
* @param mixed $name
* @return mixed
*/
public function viewVar($name = null) {
if (empty($name)) {
return $this->config('viewVar') ?: Inflector::variable($this->_model()->name);
}
return $this->config('viewVar', $name);
}
/**
* HTTP GET handler
*
* @throws NotFoundException If record not found
* @param string $id
* @return void
*/
protected function _get($id = null) {
if (!$this->_validateId($id)) {
return false;
}
$model = $this->_model();
$query = array();
$query['conditions'] = array($model->escapeField() => $id);
$findMethod = $this->_getFindMethod('first');
$subject = $this->_trigger('beforeFind', compact('id', 'query', 'findMethod'));
$item = $model->find($subject->findMethod, $subject->query);
if (empty($item)) {
$this->_trigger('recordNotFound', compact('id'));
$message = $this->message('recordNotFound', array('id' => $id));
$exceptionClass = $message['class'];
throw new $exceptionClass($message['text'], $message['code']);
}
$success = true;
$viewVar = $this->viewVar();
$subject = $this->_trigger('afterFind', compact('id', 'viewVar', 'success', 'item'));
$this->_controller()->set(array('success' => $subject->success, $subject->viewVar => $subject->item));
$this->_trigger('beforeRender', $subject);
}
}

View File

@ -1,441 +0,0 @@
<?php
App::uses('CrudBaseObject', 'Crud.Controller/Crud');
App::uses('Hash', 'Utility');
App::uses('Validation', 'Utility');
/**
* Base Crud class
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
abstract class CrudAction extends CrudBaseObject {
/**
* Constant representing a model-level action
*
* @var integer
*/
const SCOPE_MODEL = 0;
/**
* Constant representing a record-level action
*
* @var integer
*/
const SCOPE_RECORD = 1;
/**
* Startup method
*
* Called when the action is loaded
*
* @param CrudSubject $subject
* @param array $defaults
* @return void
*/
public function __construct(CrudSubject $subject, array $defaults = array()) {
parent::__construct($subject, $defaults);
$this->_settings['action'] = $subject->action;
}
/**
* Handle callback
*
* Based on the requested controller action,
* decide if we should handle the request or not.
*
* By returning false the handling is cancelled and the
* execution flow continues
*
* @throws NotImplementedException if the action can't handle the request
* @param CakeEvent $event
* @return mixed
*/
public function handle(CrudSubject $subject) {
if (!$this->config('enabled')) {
return false;
}
$requestMethod = $this->_request()->method();
$method = '_' . strtolower($requestMethod);
if (method_exists($this, $method)) {
return call_user_func_array(array($this, $method), $subject->args);
}
if (method_exists($this, '_handle')) {
return call_user_func_array(array($this, '_handle'), $subject->args);
}
throw new NotImplementedException(sprintf('Action %s does not implement a handler for HTTP verb %s', get_class($this), $requestMethod));
}
/**
* Disable the Crud action
*
* @return void
*/
public function disable() {
$this->config('enabled', false);
$Controller = $this->_controller();
$actionName = $this->config('action');
$pos = array_search($actionName, $Controller->methods);
if ($pos !== false) {
unset($Controller->methods[$pos]);
}
}
/**
* Enable the Crud action
*
* @return void
*/
public function enable() {
$this->config('enabled', true);
$Controller = $this->_controller();
$actionName = $this->config('action');
if (!in_array($actionName, $Controller->methods)) {
$Controller->methods[] = $actionName;
}
}
/**
* Change the find() method
*
* If `$method` is NULL the current value is returned
* else the `findMethod` is changed
*
* @param mixed $method
* @return mixed
*/
public function findMethod($method = null) {
if ($method === null) {
return $this->config('findMethod');
}
return $this->config('findMethod', $method);
}
/**
* Change the save() method
*
* If `$method` is NULL the current value is returned
* else the `saveMethod` is changed
*
* @param mixed $method
* @return mixed
*/
public function saveMethod($method = null) {
if ($method === null) {
return $this->config('saveMethod');
}
return $this->config('saveMethod', $method);
}
/**
* Set or get the related models that should be found
* for the action
*
* @param mixed $related Everything but `null` will change the configuration
* @return mixed
*/
public function relatedModels($related = null) {
if ($related === null) {
return $this->config('relatedModels');
}
return $this->config('relatedModels', $related, false);
}
/**
* Change redirect configuration
*
* If both `$name` and `$config` is empty all redirection
* rules will be returned.
*
* If `$name` is provided and `$config` is null, the named
* redirection configuration is returned.
*
* If both `$name` and `$config` is provided, the configuration
* is changed for the named rule.
*
* $config should contain the following keys:
* - type : name of the reader
* - key : the key to read inside the reader
* - url : the URL to redirect to
*
* @param null|string $name Name of the redirection rule
* @param null|array $config Redirection configuration
* @return mixed
*/
public function redirectConfig($name = null, $config = null) {
if ($name === null && $config === null) {
return $this->config('redirect');
}
$path = sprintf('redirect.%s', $name);
if ($config === null) {
return $this->config($path);
}
return $this->config($path, $config);
}
/**
* return the config for a given message type
*
* @param string $type
* @param array $replacements
* @return array
* @throws CakeException for a missing or undefined message type
*/
public function message($type, array $replacements = array()) {
if (empty($type)) {
throw new CakeException('Missing message type');
}
$crud = $this->_crud();
$config = $this->config('messages.' . $type);
if (empty($config)) {
$config = $crud->config('messages.' . $type);
if (empty($config)) {
throw new CakeException(sprintf('Invalid message type "%s"', $type));
}
}
if (is_string($config)) {
$config = array('text' => $config);
}
$config = Hash::merge(array(
'element' => 'default',
'params' => array('class' => 'message'),
'key' => 'flash',
'type' => $this->config('action') . '.' . $type,
'name' => $this->_getResourceName()
), $config);
if (!isset($config['text'])) {
throw new CakeException(sprintf('Invalid message config for "%s" no text key found', $type));
}
$config['params']['original'] = ucfirst(str_replace('{name}', $config['name'], $config['text']));
$domain = $this->config('messages.domain');
if (!$domain) {
$domain = $crud->config('messages.domain') ?: 'crud';
}
$config['text'] = __d($domain, $config['params']['original']);
$config['text'] = String::insert(
$config['text'],
$replacements + array('name' => $config['name']),
array('before' => '{', 'after' => '}')
);
$config['params']['class'] .= ' ' . $type;
return $config;
}
/**
* Change the saveOptions configuration
*
* This is the 2nd argument passed to saveAll()
*
* if `$config` is NULL the current config is returned
* else the `saveOptions` is changed
*
* @param mixed $config
* @return mixed
*/
public function saveOptions($config = null) {
if (empty($config)) {
return $this->config('saveOptions');
}
return $this->config('saveOptions', $config);
}
/**
* Change the view to be rendered
*
* If `$view` is NULL the current view is returned
* else the `$view` is changed
*
* If no view is configured, it will use the action
* name from the request object
*
* @param mixed $view
* @return mixed
*/
public function view($view = null) {
if (empty($view)) {
return $this->config('view') ?: $this->_request()->action;
}
return $this->config('view', $view);
}
/**
* List of implemented events
*
* @return array
*/
public function implementedEvents() {
return array();
}
/**
* Get the model find method for a current controller action
*
* @param string $default The default find method in case it hasn't been mapped
* @return string The find method used in ->_model->find($method)
*/
protected function _getFindMethod($default = null) {
$findMethod = $this->findMethod();
if (!empty($findMethod)) {
return $findMethod;
}
return $default;
}
/**
* Wrapper for Session::setFlash
*
* @param string $type Message type
* @return void
*/
public function setFlash($type) {
$config = $this->message($type);
$subject = $this->_trigger('setFlash', $config);
if (!empty($subject->stopped)) {
return;
}
$this->_session()->setFlash($subject->text, $subject->element, $subject->params, $subject->key);
}
/**
* Automatically detect primary key data type for `_validateId()`
*
* Binary or string with length of 36 chars will be detected as UUID
* If the primary key is a number, integer validation will be used
*
* If no reliable detection can be made, no validation will be made
*
* @param Model $model
* @return string
* @throws CakeException If unable to get model object
*/
public function detectPrimaryKeyFieldType(Model $model = null) {
if (empty($model)) {
$model = $this->_model();
if (empty($model)) {
throw new CakeException('Missing model object, cant detect primary key field type');
}
}
$fInfo = $model->schema($model->primaryKey);
if (empty($fInfo)) {
return false;
}
if ($fInfo['length'] == 36 && ($fInfo['type'] === 'string' || $fInfo['type'] === 'binary')) {
return 'uuid';
}
if ($fInfo['type'] === 'integer') {
return 'integer';
}
return false;
}
/**
* Return the human name of the model
*
* By default it uses Inflector::humanize, but can be changed
* using the "name" configuration property
*
* @return string
*/
protected function _getResourceName() {
if (empty($this->_settings['name'])) {
$this->_settings['name'] = strtolower(Inflector::humanize(Inflector::underscore($this->_model()->name)));
}
return $this->_settings['name'];
}
/**
* Is the passed ID valid ?
*
* By default we assume you want to validate an numeric string
* like a normal incremental ids from MySQL
*
* Change the validateId settings key to "uuid" for UUID check instead
*
* @param mixed $id
* @return boolean
* @throws BadRequestException If id is invalid
*/
protected function _validateId($id) {
$type = $this->config('validateId');
if ($type === null) {
$type = $this->detectPrimaryKeyFieldType();
}
if (!$type) {
return true;
} elseif ($type === 'uuid') {
$valid = Validation::uuid($id);
} else {
$valid = is_numeric($id);
}
if ($valid) {
return true;
}
$subject = $this->_trigger('invalidId', compact('id'));
$message = $this->message('invalidId');
$exceptionClass = $message['class'];
throw new $exceptionClass($message['text'], $message['code']);
}
/**
* Called for all redirects inside CRUD
*
* @param CrudSubject $subject
* @param string|array $url
* @param integer $status
* @param boolean $exit
* @return void
*/
protected function _redirect(CrudSubject $subject, $url = null, $status = null, $exit = true) {
$url = $this->_redirectUrl($url);
$subject->url = $url;
$subject->status = $status;
$subject->exit = $exit;
$subject = $this->_trigger('beforeRedirect', $subject);
$controller = $this->_controller();
$controller->redirect($subject->url, $subject->status, $subject->exit);
return $controller->response;
}
}

View File

@ -1,277 +0,0 @@
<?php
App::uses('CakeEventListener', 'Event');
App::uses('Hash', 'Utility');
/**
* Crud Base Class
*
* Implement base methods used in CrudAction and CrudListener classes
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
abstract class CrudBaseObject extends Object implements CakeEventListener {
/**
* Container with reference to all objects
* needed within the CrudListener and CrudAction
*
* @var CrudSubject
*/
protected $_container;
/**
* Instance configuration
*
* @var array
*/
protected $_settings = array();
/**
* Constructor
*
* @param CrudSubject $subject
* @param array $defaults Default settings
* @return void
*/
public function __construct(CrudSubject $subject, $defaults = array()) {
$this->_container = $subject;
if (!empty($defaults)) {
$this->config($defaults);
}
}
/**
* initialize callback
*
* @param CakeEvent $event
* @return void
*/
public function beforeHandle(CakeEvent $event) {
$this->_container = $event->subject;
}
/**
* Sets a configuration variable into this action
*
* If called with no arguments, all configuration values are
* returned.
*
* $key is interpreted with dot notation, like the one used for
* Configure::write()
*
* If $key is string and $value is not passed, it will return the
* value associated with such key.
*
* If $key is an array and $value is empty, then $key will
* be interpreted as key => value dictionary of settings and
* it will be merged directly with $this->settings
*
* If $key is a string, the value will be inserted in the specified
* slot as indicated using the dot notation
*
* @param mixed $key
* @param mixed $value
* @param boolean $merge
* @return mixed|CrudAction
*/
public function config($key = null, $value = null, $merge = true) {
if ($key === null && $value === null) {
return $this->_settings;
}
if ($value === null) {
if (is_array($key)) {
if ($merge) {
$this->_settings = Hash::merge($this->_settings, $key);
} else {
foreach (Hash::flatten($key) as $k => $v) {
$this->_settings = Hash::insert($this->_settings, $k, $v);
}
}
return $this;
}
return Hash::get($this->_settings, $key);
}
if (is_array($value)) {
if ($merge) {
$value = array_merge((array)Hash::get($this->_settings, $key), $value);
} else {
foreach ($value as $k => $v) {
$this->_settings = Hash::insert($this->_settings, $k, $v);
}
}
}
$this->_settings = Hash::insert($this->_settings, $key, $value);
return $this;
}
/**
* Returns a list of all events that will fire during the objects lifecycle.
* You can override this function to add you own listener callbacks
*
* @return array
*/
public function implementedEvents() {
return array(
'Crud.initialize' => 'initialize'
);
}
/**
* Proxy method for `$this->_crud()->action()`
*
* Primarily here to ease unit testing
*
* @codeCoverageIgnore
* @param string $name
* @return CrudAction
*/
protected function _action($name = null) {
return $this->_crud()->action($name);
}
/**
* Proxy method for `$this->_crud()->trigger()`
*
* Primarily here to ease unit testing
*
* @codeCoverageIgnore
* @param string $eventName
* @param array $data
* @return CrudSubject
*/
protected function _trigger($eventName, $data = array()) {
return $this->_crud()->trigger($eventName, $data);
}
/**
* Proxy method for `$this->_crud()->listener()`
*
* Primarily here to ease unit testing
*
* @codeCoverageIgnore
* @param string $name
* @return CrudListener
*/
protected function _listener($name) {
return $this->_crud()->listener($name);
}
/**
* Proxy method for `$this->_crud()->Session`
*
* Primarily here to ease unit testing
*
* @codeCoverageIgnore
* @return SessionComponent
*/
protected function _session() {
return $this->_crud()->Session;
}
/**
* Proxy method for `$this->_container->_controller`
*
* Primarily here to ease unit testing
*
* @codeCoverageIgnore
* @return Controller
*/
protected function _controller() {
return $this->_container->controller;
}
/**
* Proxy method for `$this->_container->_request`
*
* Primarily here to ease unit testing
*
* @codeCoverageIgnore
* @return CakeRequest
*/
protected function _request() {
return $this->_container->request;
}
/**
* Proxy method for `$this->_container->_model`
*
* Primarily here to ease unit testing
*
* @codeCoverageIgnore
* @return Model
*/
protected function _model() {
return $this->_container->model;
}
/**
* Proxy method for `$this->_crud()->getSubject()`
*
* @codeCoverageIgnore
* @param array $additional
* @return CrudSUbject
*/
protected function _subject($additional = array()) {
return $this->_crud()->getSubject($additional);
}
/**
* Proxy method for `$this->_container->_crud`
*
* @return CrudComponent
*/
protected function _crud() {
return $this->_container->crud;
}
/**
* Proxy method for `$this->_crud()->validationErrors()`
*
* Primarily here to ease unit testing
*
* @codeCoverageIgnore
* @return array
*/
protected function _validationErrors() {
return $this->_crud()->validationErrors();
}
/**
* Returns the redirect_url for this request, with a fallback to the referring page
*
* @param string $default Default URL to use redirect_url is not found in request or data
* @param boolean $local If true, restrict referring URLs to local server
* @return mixed
*/
protected function _refererRedirectUrl($default = null) {
$controller = $this->_controller();
return $this->_redirectUrl($controller->referer($default, true));
}
/**
* Returns the redirect_url for this request.
*
* @param string $default Default URL to use redirect_url is not found in request or data
* @return mixed
*/
protected function _redirectUrl($default = null) {
$url = $default;
$request = $this->_request();
if (!empty($request->data['redirect_url'])) {
$url = $request->data['redirect_url'];
} elseif (!empty($request->query['redirect_url'])) {
$url = $request->query['redirect_url'];
}
return $url;
}
}

View File

@ -1,67 +0,0 @@
<?php
App::uses('CrudBaseObject', 'Crud.Controller/Crud');
/**
* The Base Crud Listener
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*
* @codeCoverageIgnore
*/
abstract class CrudListener extends CrudBaseObject {
/**
* Returns a list of all events that will fire in the controller during its life cycle.
* You can override this function to add you own listener callbacks.
*
* - initialize: Called at the same time as CrudComponent::initialize()
* - startup: Called at the same time as CrudComponent::startup()
* - beforeHandle : Called before CrudAction is executed
* - recordNotFound : Called if a find() did not return any records
* - beforePaginate : Called right before any paginate() method
* - afterPaginate : Called right after any paginate() method
* - invalidId : Called if the ID format validation failed
* - setFlash : Called before any CakeSession::setFlash()
*
* @return array
*/
public function implementedEvents() {
$eventMap = array(
'Crud.initialize' => 'initialize',
'Crud.startup' => 'startup',
'Crud.beforeHandle' => 'beforeHandle',
'Crud.beforePaginate' => 'beforePaginate',
'Crud.afterPaginate' => 'afterPaginate',
'Crud.recordNotFound' => 'recordNotFound',
'Crud.invalidId' => 'invalidId',
'Crud.setFlash' => 'setFlash',
'Crud.beforeRender' => 'beforeRender',
'Crud.beforeRedirect' => 'beforeRedirect',
'Crud.beforeSave' => 'beforeSave',
'Crud.afterSave' => 'afterSave',
'Crud.beforeFind' => 'beforeFind',
'Crud.afterFind' => 'afterFind',
'Crud.beforeDelete' => 'beforeDelete',
'Crud.afterDelete' => 'afterDelete',
);
$events = array();
foreach ($eventMap as $event => $method) {
if (method_exists($this, $method)) {
$events[$event] = $method;
}
}
return $events;
}
}

View File

@ -1,157 +0,0 @@
<?php
/**
* Crud subject
*
* All Crud.* events passes this object as subject
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class CrudSubject {
/**
* Instance of the crud component
*
* @var CrudComponent
*/
public $crud;
/**
* Instance of the controller
*
* @var Controller
*/
public $controller;
/**
* Name of the default controller model class
*
* @var string
*/
public $modelClass;
/**
* The default action model instance
*
* @var Model
*/
public $model;
/**
* Request object instance
*
* @var CakeRequest
*/
public $request;
/**
* Response object instance
*
* @var CakeResponse
*/
public $response;
/**
* The name of the action object associated with this dispatch
*
* @var string
*/
public $action;
/**
* Optional arguments passed to the controller action
*
* @var array
*/
public $args;
/**
* List of events this subject has passed through
*
* @var array
*/
protected $_events = array();
/**
* Constructor
*
* @param array $fields
* @return void
*/
public function __construct($fields = array()) {
$this->set($fields);
}
/**
* Add an event name to the list of events this subject has passed through
*
* @param string $name name of event
* @return void
*/
public function addEvent($name) {
$this->_events[] = $name;
}
/**
* Returns the list of events this subject has passed through
*
* @return array
*/
public function getEvents() {
return $this->_events;
}
/**
* Returns whether the specified event is in the list of events
* this subject has passed through
*
* @param string $name name of event
* @return array
*/
public function hasEvent($name) {
return in_array($name, $this->_events);
}
/**
* Set a list of key / values for this object
*
* @param array $fields
* @return void
*/
public function set($fields) {
foreach ($fields as $k => $v) {
$this->{$k} = $v;
}
}
/**
* Check if the called action is white listed or blacklisted
* depending on the mode
*
* Modes:
* only => only if in array (white list)
* not => only if NOT in array (blacklist)
*
* @param string $mode
* @param mixed $actions
* @return boolean
* @throws CakeException In case of invalid mode
*/
public function shouldProcess($mode, $actions = array()) {
if (is_string($actions)) {
$actions = array($actions);
}
switch ($mode) {
case 'only':
return in_array($this->action, $actions);
case 'not':
return !in_array($this->action, $actions);
default:
throw new CakeException('Invalid mode');
}
}
}

View File

@ -1,348 +0,0 @@
<?php
App::uses('CrudListener', 'Crud.Controller/Crud');
/**
* Field Filter Listener
*
* Allow the requester to decide what fields and relations that should be
* returned by providing a `fields` GET argument with a comma separated list of fields.
*
* For a relation automatically to be joined, it has to be whitelisted first.
* If no whitelist exists, no relations will be added automatically
* `$this->_action()->config('apiFieldFilter.models', array('list', 'of', 'models'))`
*
* You can also whitelist fields, if no whitelist exists for fields, all fields are allowed
* If whitelisting exists, only those fields will be allowed to be selected.
* The fields must be in `Model.field` format
* `$this->_action()->config('apiFieldFilter.fields.whitelist', array('Model.id', 'Model.name', 'Model.created'))`
*
* You can also blacklist fields, if no blacklist exists, no blacklisting is done
* If blacklisting exists, the field will be removed from the field list if present
* The fields must be in `Model.field` format
* `$this->_action()->config('apiFieldFilter.fields.blacklist', array('Model.password', 'Model.auth_token', 'Model.created'))`
*
* This is probably only useful if it's used in conjunction with the ApiListener
*
* Limitation: Related models is only supported in 1 level away from the primary model at
* this time. E.g. "Blog" => Auth, Tag, Posts
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class ApiFieldFilterListener extends CrudListener {
/**
* Returns a list of all events that will fire in the controller during its lifecycle.
* You can override this function to add you own listener callbacks
*
* We attach at priority 50 so normal bound events can run before us
*
* @return array
*/
public function implementedEvents() {
return array(
'Crud.beforePaginate' => array('callable' => 'beforePaginate', 'priority' => 50),
'Crud.beforeFind' => array('callable' => 'beforeFind', 'priority' => 50)
);
}
/**
* List of relations that should be contained
*
* @var array
*/
protected $_relations = array();
/**
* beforeFind
*
* @param CakeEvent $event
* @return void
*/
public function beforeFind(CakeEvent $event) {
if (!$this->_request()->is('api')) {
return;
}
$fields = $this->_getFields($event);
if (empty($fields)) {
return;
}
$event->subject->query['fields'] = array_unique($fields);
$event->subject->query['contain'] = $this->_relations;
}
/**
* beforePaginate
*
* @param CakeEvent $event
* @return void
*/
public function beforePaginate(CakeEvent $event) {
if (!$this->_request()->is('api')) {
return;
}
$fields = $this->_getFields($event);
if (empty($fields)) {
return;
}
$controller = $this->_controller();
$controller->Paginator->settings['fields'] = $fields;
$controller->Paginator->settings['contain'] = $this->_relations;
}
/**
* Whitelist fields that are allowed to be included in the
* output list of fields
*
* @param array $fields
* @param string $action
* @return mixed
*/
public function whitelistFields($fields = null, $action = null) {
if (empty($fields)) {
return $this->_action($action)->config('apiFieldFilter.fields.whitelist');
}
$this->_action($action)->config('apiFieldFilter.fields.whitelist', $fields);
}
/**
* Blacklist fields that are not allowed to be included in the
* output list of fields
*
* @param array $fields
* @param string $action
* @return mixed
*/
public function blacklistFields($fields = null, $action = null) {
if (empty($fields)) {
return $this->_action($action)->config('apiFieldFilter.fields.blacklist');
}
$this->_action($action)->config('apiFieldFilter.fields.blacklist', $fields);
}
/**
* Whitelist associated models that are allowed to be included in the
* output list of fields
*
* @param array $models
* @param string $action
* @return mixed
*/
public function whitelistModels($models = null, $action = null) {
if (empty($models)) {
return $this->_action($action)->config('apiFieldFilter.models.whitelist');
}
$this->_action($action)->config('apiFieldFilter.models.whitelist', $models);
}
/**
* Can the client make a request without specifying the fields he wants
* returned?
*
* This will bypass all black- and white- listing if set to true
*
* @param boolean $permit
* @param string $action
* @return boolean
*/
public function allowNoFilter($permit = null, $action = null) {
if (empty($permit)) {
return (bool)$this->_action($action)->config('apiFieldFilter.allowNoFilter');
}
$this->_action($action)->config('apiFieldFilter.allowNoFilter', (bool)$permit);
}
/**
* Get fields for the query
*
* @param CakeEvent $event
* @return array
* @throws CakeException If fields not specified
*/
protected function _getFields(CakeEvent $event) {
$this->_relations = array();
$fields = $this->_getFieldsForQuery($this->_model());
if (empty($fields) && !$this->allowNoFilter(null, $event->subject->action)) {
throw new CakeException('Please specify which fields you would like to select');
}
return $fields;
}
/**
* Get the list of fields that should be selected
* in the query based on the HTTP GET requests fields
*
* @param Model $model
* @return array
*/
protected function _getFieldsForQuery(Model $model) {
$fields = $this->_getFieldsFromRequest();
if (empty($fields)) {
return;
}
$newFields = array();
foreach ($fields as $field) {
$fieldName = $this->_checkField($model, $field);
// The field should not be included in the query
if (empty($fieldName)) {
continue;
}
$newFields[] = $fieldName;
}
return $newFields;
}
/**
* Get a list of fields from the HTTP request
*
* It's assumed the fields are comma separated
*
* @return array
*/
protected function _getFieldsFromRequest() {
$query = $this->_request()->query;
if (empty($query['fields'])) {
return;
}
return array_unique(array_filter(explode(',', $query['fields'])));
}
/**
* Secure a field - check that the field exists in the model
* or a closely related model
*
* If the field doesn't exist, it's removed from the
* field list.
*
* @param Model $model
* @param string $field
* @return mixed
*/
protected function _checkField(Model $model, $field) {
list ($modelName, $fieldName) = pluginSplit($field, false);
// Prefix fields that don't have a model key with the local model name
if (empty($modelName)) {
$modelName = $model->alias;
}
$isPrimary = $modelName === $model->alias;
// If the model name is the local one, check if the field exists
if ($isPrimary && !$model->hasField($fieldName)) {
return false;
}
// Check associated models if the field exists there
if (!$isPrimary) {
if (!$this->_associatedModelHasField($model, $modelName, $fieldName)) {
return false;
}
}
$fullFieldName = sprintf('%s.%s', $modelName, $fieldName);
if (!$this->_whitelistedField($fullFieldName)) {
return;
}
if ($this->_blacklistedField($fullFieldName)) {
return;
}
if (!$isPrimary) {
$this->_relations[] = $modelName;
}
return $fullFieldName;
}
/**
* Check if the associated `modelName` to the `$model`
* exists and if it has the field in question
*
* @param Model $model
* @param string $modelName
* @param string $fieldName
* @return boolean
*/
protected function _associatedModelHasField(Model $model, $modelName, $fieldName) {
$associated = $model->getAssociated();
if (!array_key_exists($modelName, $associated)) {
return false;
}
if (!$this->_whitelistedAssociatedModel($modelName)) {
return false;
}
return $model->{$modelName}->hasField($fieldName);
}
/**
* Check if the associated model is whitelisted to be automatically
* contained on demand or not
*
* If no whitelisting exists, no associated models may be joined
*
* @param string $modelName
* @return boolean
*/
protected function _whitelistedAssociatedModel($modelName) {
$allowedModels = $this->whitelistModels();
if (empty($allowedModels)) {
return false;
}
return in_array($modelName, $allowedModels);
}
/**
* Check if a field has been whitelisted
*
* If no field whitelisting has been done, all fields
* are allowed to be selected
*
* @param string $fieldName
* @return boolean
*/
protected function _whitelistedField($fieldName) {
$allowedFields = $this->whitelistFields();
if (empty($allowedFields)) {
return true;
}
return in_array($fieldName, $allowedFields);
}
/**
* Check if a field has been blacklisted
*
* @param string $fieldName
* @return boolean
*/
protected function _blacklistedField($fieldName) {
$disallowedFields = $this->blacklistFields();
if (empty($disallowedFields)) {
return false;
}
return in_array($fieldName, $disallowedFields);
}
}

View File

@ -1,443 +0,0 @@
<?php
App::uses('CrudListener', 'Crud.Controller/Crud');
App::uses('CrudValidationException', 'Crud.Error/Exception');
/**
* Enabled Crud to respond in a computer readable format like JSON or XML
*
* It tries to enforce some REST principles and keep some string conventions in the output format
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class ApiListener extends CrudListener {
/**
* Default configuration
*
* @var array
*/
protected $_settings = array(
'viewClasses' => array(
'json' => 'Crud.CrudJson',
'xml' => 'Crud.CrudXml'
),
'detectors' => array(
'json' => array('ext' => 'json', 'accepts' => 'application/json'),
'xml' => array('ext' => 'xml', 'accepts' => 'text/xml')
),
'exception' => array(
'type' => 'default',
'class' => 'BadRequestException',
'message' => 'Unknown error',
'code' => 0
)
);
/**
* Returns a list of all events that will fire in the controller during its lifecycle.
* You can override this function to add you own listener callbacks
*
* We attach at priority 10 so normal bound events can run before us
*
* @return array
*/
public function implementedEvents() {
return array(
'Crud.beforeHandle' => array('callable' => 'beforeHandle', 'priority' => 10),
'Crud.setFlash' => array('callable' => 'setFlash', 'priority' => 5),
'Crud.beforeRender' => array('callable' => 'respond', 'priority' => 100),
'Crud.beforeRedirect' => array('callable' => 'respond', 'priority' => 100)
);
}
/**
* setup
*
* Called when the listener is created
*
* @return void
*/
public function setup() {
$this->setupDetectors();
$this->registerExceptionHandler();
}
/**
* beforeHandle
*
* Called before the crud action is executed
*
* @param CakeEvent $event
* @return void
*/
public function beforeHandle(CakeEvent $event) {
parent::beforeHandle($event);
if (!$this->_request()->is('api')) {
$events = $this->implementedEvents();
$eventManager = $this->_controller()->getEventManager();
foreach (array_keys($events) as $name) {
if ($name === 'Crud.beforeHandle') {
continue;
}
$eventManager->detach($this, $name);
}
return;
}
$this->_checkRequestMethods();
}
/**
* Check for allowed HTTP request types
*
* @throws BadRequestException
* @return boolean
*/
protected function _checkRequestMethods() {
$action = $this->_action();
$apiConfig = $action->config('api');
if (!isset($apiConfig['methods'])) {
return false;
}
$request = $this->_request();
foreach ($apiConfig['methods'] as $method) {
if ($request->is($method)) {
return true;
}
}
throw new BadRequestException('Wrong request method');
}
/**
* Register the Crud exception handler
*
* @return void
*/
public function registerExceptionHandler() {
if (!$this->_request()->is('api')) {
return;
}
App::uses('CrudExceptionRenderer', 'Crud.Error');
Configure::write('Exception.renderer', 'Crud.CrudExceptionRenderer');
}
/**
* Handle response
*
* @param CakeEvent $event
* @return CakeResponse
*/
public function respond(CakeEvent $event) {
$subject = $event->subject;
$action = $this->_action();
$key = $subject->success ? 'success' : 'error';
$apiConfig = $action->config('api.' . $key);
if (isset($apiConfig['exception'])) {
return $this->_exceptionResponse($apiConfig['exception']);
}
$response = $this->render($event->subject);
$response->statusCode($apiConfig['code']);
return $response;
}
/**
* Throw an exception based on API configuration
*
* @throws CakeException
* @param array $exceptionConfig
* @return void
*/
protected function _exceptionResponse($exceptionConfig) {
$exceptionConfig = array_merge($this->config('exception'), $exceptionConfig);
$class = $exceptionConfig['class'];
if ($exceptionConfig['type'] === 'validate') {
$errors = $this->_validationErrors();
throw new $class($errors);
}
throw new $class($exceptionConfig['message'], $exceptionConfig['code']);
}
/**
* Selects an specific Crud view class to render the output
*
* @param CrudSubject $subject
* @return CakeResponse
*/
public function render(CrudSubject $subject) {
$this->injectViewClasses();
$this->_ensureSuccess($subject);
$this->_ensureData($subject);
$this->_ensureSerialize();
$controller = $this->_controller();
if (!empty($controller->RequestHandler->ext)) {
$controller->RequestHandler->renderAs($controller, $controller->RequestHandler->ext);
}
return $controller->render();
}
/**
* Ensure _serialize is set in the view
*
* @return void
*/
protected function _ensureSerialize() {
$controller = $this->_controller();
if (isset($controller->viewVars['_serialize'])) {
return;
}
$action = $this->_action();
$serialize = array();
$serialize[] = 'success';
if (method_exists($action, 'viewVar')) {
$serialize['data'] = $action->viewVar();
} else {
$serialize[] = 'data';
}
$serialize = array_merge($serialize, (array)$action->config('serialize'));
$controller->set('_serialize', $serialize);
}
/**
* Ensure success key is present in Controller::$viewVars
*
* @param CrudSubject $subject
* @return void
*/
protected function _ensureSuccess(CrudSubject $subject) {
$controller = $this->_controller();
if (isset($controller->viewVars['success'])) {
return;
}
$controller->set('success', $subject->success);
}
/**
* Ensure data key is present in Controller:$viewVars
*
* @param CrudSubject $subject
* @return void
*/
protected function _ensureData(CrudSubject $subject) {
$controller = $this->_controller();
// Don't touch existing data properties
if (isset($controller->viewVars['data'])) {
return;
}
$key = $subject->success ? 'success' : 'error';
// Load configuration
$config = $this->_action()->config('api.' . $key);
// New, empty, data array
$data = array();
// If fields should be extracted from the subject
if (isset($config['data']['subject'])) {
$config['data']['subject'] = Hash::normalize((array)$config['data']['subject']);
$subjectArray = (array)$subject;
foreach ($config['data']['subject'] as $keyPath => $valuePath) {
if ($valuePath === null) {
$valuePath = $keyPath;
}
$keyPath = $this->_expandPath($subject, $keyPath);
$valuePath = $this->_expandPath($subject, $valuePath);
$data = Hash::insert($data, $keyPath, Hash::get($subjectArray, $valuePath));
}
}
// Raw (hardcoded) key/values
if (isset($config['data']['raw'])) {
foreach ($config['data']['raw'] as $path => $value) {
$path = $this->_expandPath($subject, $path);
$data = Hash::insert($data, $path, $value);
}
}
// Publish the new data
$controller->set('data', $data);
}
/**
* Expand all scalar values from a CrudSubject
* and use them for a String::insert() interpolation
* of a path
*
* @param CrudSubject $subject
* @param string $path
* @return string
*/
protected function _expandPath(CrudSubject $subject, $path) {
$keys = array();
$subjectArray = (array)$subject;
foreach (array_keys($subjectArray) as $key) {
if (!is_scalar($subjectArray[$key])) {
continue;
}
$keys[$key] = $subjectArray[$key];
}
return String::insert($path, $keys, array('before' => '{', 'after' => '}'));
}
/**
* Inject view classes into RequestHandler
*
* @see http://book.cakephp.org/2.0/en/core-libraries/components/request-handling.html#using-custom-viewclasses
* @return void
*/
public function injectViewClasses() {
$controller = $this->_controller();
foreach ($this->config('viewClasses') as $type => $class) {
$controller->RequestHandler->viewClassMap($type, $class);
}
}
/**
* Get or set a viewClass
*
* `$type` could be `json`, `xml` or any other valid type
* defined by the `RequestHandler`
*
* `$class` could be any View class capable of handling
* the response format for the `$type`. Normal
* CakePHP plugin "dot" notation is supported
*
* @see http://book.cakephp.org/2.0/en/core-libraries/components/request-handling.html#using-custom-viewclasses
* @param string $type
* @param string $class
* @return mixed
*/
public function viewClass($type, $class = null) {
if ($class === null) {
return $this->config('viewClasses.' . $type);
}
return $this->config('viewClasses.' . $type, $class);
}
/**
* setFlash
*
* An API request doesn't need flash messages - so stop them being processed
*
* @param CakeEvent $event
*/
public function setFlash(CakeEvent $event) {
$event->stopPropagation();
}
/**
* Setup detectors
*
* Both detects on two signals:
* 1) The extension in the request (e.g. /users/index.$ext)
* 2) The accepts header from the client
*
* There is a combined request detector for all detectors called 'api'
*
* @return void
*/
public function setupDetectors() {
$request = $this->_request();
$detectors = $this->config('detectors');
foreach ($detectors as $name => $config) {
$request->addDetector($name, array('callback' => function(CakeRequest $request) use ($config) {
if (isset($request->params['ext']) && $request->params['ext'] === $config['ext']) {
return true;
}
return $request->accepts($config['accepts']);
}));
}
$request->addDetector('api', array('callback' => function(CakeRequest $request) use ($detectors) {
foreach ($detectors as $name => $config) {
if ($request->is($name)) {
return true;
}
}
return false;
}));
}
/**
* Automatically create REST resource routes for all controllers found in your main
* application or in a specific plugin to provide access to your resources
* using /controller/id.json instead of the default /controller/view/id.json.
*
* If called with no arguments, all controllers in the main application will be mapped.
* If called with a valid plugin name all controllers in that plugin will be mapped.
* If combined both controllers from the application and the plugin(s) will be mapped.
*
* This function needs to be called from your application's app/Config/routes.php:
*
* ```
* App::uses('ApiListener', 'Crud.Controller/Crud/Listener');
*
* ApiListener::mapResources();
* ApiListener::mapResources('DebugKit');
* Router::setExtensions(array('json', 'xml'));
* Router::parseExtensions();
* ```
*
* @static
* @param string $plugin
* @return void
*/
public static function mapResources($plugin = null) {
$key = 'Controller';
if ($plugin) {
$key = $plugin . '.Controller';
}
$controllers = array();
foreach (App::objects($key) as $controller) {
if ($controller !== $plugin . 'AppController') {
if ($plugin) {
$controller = $plugin . '.' . $controller;
}
array_push($controllers, str_replace('Controller', '', $controller));
}
}
Router::mapResources($controllers);
}
}

View File

@ -1,59 +0,0 @@
<?php
App::uses('CrudListener', 'Crud.Controller/Crud');
/**
* When loaded Crud API Pagination Listener will include
* pagination information in the response
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class ApiPaginationListener extends CrudListener {
/**
* Returns a list of all events that will fire in the controller during its lifecycle.
* You can override this function to add you own listener callbacks
*
* We attach at priority 10 so normal bound events can run before us
*
* @return array
*/
public function implementedEvents() {
return array(
'Crud.beforeRender' => array('callable' => 'beforeRender', 'priority' => 75)
);
}
/**
* Appends the pagination information to the JSON or XML output
*
* @param CakeEvent $event
* @return void
*/
public function beforeRender(CakeEvent $event) {
$request = $this->_request();
if (!$request->is('api')) {
return;
}
$_pagination = $request->paging;
if (empty($_pagination) || !array_key_exists($event->subject->modelClass, $_pagination)) {
return;
}
$_pagination = $_pagination[$event->subject->modelClass];
$pagination = array(
'page_count' => $_pagination['pageCount'],
'current_page' => $_pagination['page'],
'has_next_page' => $_pagination['nextPage'],
'has_prev_page' => $_pagination['prevPage'],
'count' => $_pagination['count'],
'limit' => $_pagination['limit']
);
$this->_action()->config('serialize.pagination', 'pagination');
$this->_controller()->set('pagination', $pagination);
}
}

View File

@ -1,100 +0,0 @@
<?php
App::uses('ConnectionManager', 'Model');
App::uses('CrudListener', 'Crud.Controller/Crud');
/**
* When loaded Crud API will include query logs in the response
*
* Very much like the DebugKit version, the SQL log will only be appended
* if the following conditions is true:
* 1) The request must be 'api' (.json/.xml)
* 2) The debug level must be 2 or above
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class ApiQueryLogListener extends CrudListener {
/**
* Returns a list of all events that will fire in the controller during its lifecycle.
* You can override this function to add you own listener callbacks
*
* We attach at priority 10 so normal bound events can run before us
*
* @return array
*/
public function implementedEvents() {
return array(
'Crud.beforeRender' => array('callable' => 'beforeRender', 'priority' => 75)
);
}
/**
* Appends the query log to the JSON or XML output
*
* @param CakeEvent $event
* @return void
*/
public function beforeRender(CakeEvent $event) {
if (Configure::read('debug') < 2) {
return;
}
if (!$this->_request()->is('api')) {
return;
}
$this->_action()->config('serialize.queryLog', 'queryLog');
$queryLog = $this->_getQueryLogs();
$this->_controller()->set('queryLog', $queryLog);
}
/**
* Get the query logs for all sources
*
* @return array
*/
protected function _getQueryLogs() {
if (!class_exists('ConnectionManager', false)) {
return array();
}
$sources = $this->_getSources();
$queryLog = array();
foreach ($sources as $source) {
$db = $this->_getSource($source);
if (!method_exists($db, 'getLog')) {
continue;
}
$queryLog[$source] = $db->getLog(false, false);
}
return $queryLog;
}
/**
* Get a list of sources defined in database.php
*
* @codeCoverageIgnore
* @return array
*/
protected function _getSources() {
return ConnectionManager::sourceList();
}
/**
* Get a specific data source
*
* @codeCoverageIgnore
* @param string $source Datasource name
* @return DataSource
*/
protected function _getSource($source) {
return ConnectionManager::getDataSource($source);
}
}

View File

@ -1,325 +0,0 @@
<?php
/**
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
App::uses('CrudListener', 'Crud.Controller/Crud');
/**
* PublicApiListener
*
* Listener to format data consistent with most public
* APIs out there such as Twitter, GitHub and Google.
*
* - https://dev.twitter.com/docs/api/1.1/get/statuses/mentions_timeline
* - http://developer.github.com/v3/
* - https://developers.google.com/custom-search/v1/using_rest
*/
class ApiTransformationListener extends CrudListener {
/**
* Default settings.
*
* - changeNesting boolean
* Removes the top level model name and nests the associated
* records inside the primary record.
*
* - changeKeys boolean
* Changes keys to lowercase model names using the associations
* or if not empty the replaceMap setting.
*
* - changeTime boolean
* Changes datetime strings to unix time integers.
*
* - castNumbers boolean
* Casts numeric strings to the right datatype.
*
* - keyMethods array
* List of function names, closures or callback arrays to call
* for the keys when recursing through the data.
*
* - valueMethods array
* List of function names, closures or callback arrays to call
* for the values when recursing through the data.
*
* - replaceMap array
* List of key-value pairs to use for key replacement. Keep
* this empty to have it derive from the model associations.
*
* @var array
*/
protected $_settings = array(
'changeNesting' => true,
'changeKeys' => true,
'changeTime' => true,
'castNumbers' => true,
'keyMethods' => array(),
'valueMethods' => array(),
'replaceMap' => array()
);
/**
* Adds the Crud.beforeRender event. It has a high priority
* number to make sure it is called late/last.
*
* @return array
*/
public function implementedEvents() {
return array('Crud.beforeRender' => array('callable' => 'beforeRender', 'priority' => 200));
}
/**
* After everything is done and before anything is rendered change
* the data format.
*
* @return boolean
*/
public function beforeRender() {
if (!$this->_request()->is('api')) {
return true;
}
$viewVars = $this->_controller()->viewVars;
$viewVar = $this->_action()->viewVar();
if (empty($viewVars[$viewVar])) {
return true;
}
$this->_setMethods();
$alias = $this->_model()->alias;
$data = $viewVars[$viewVar];
$wrapped = false;
if (isset($data[$alias])) {
$data = array($data);
$wrapped = true;
}
$formatted = array();
foreach ($data as $index => &$record) {
$new = &$record;
if ($this->_settings['changeNesting']) {
$new = $this->_changeNesting($new, $alias);
}
unset($data[$index]);
$this->_recurse($new, $index);
$formatted[] = $new;
}
$formatted = $wrapped ? $formatted[0] : $formatted;
$this->_controller()->set($viewVar, $formatted);
return true;
}
/**
* Merge in the internal methods based on the settings.
*
* @return void
*/
protected function _setMethods() {
$keyMethods = $valueMethods = array();
if ($this->_settings['changeKeys']) {
$keyMethods[] = '_replaceKeys';
}
if ($this->_settings['castNumbers']) {
$valueMethods[] = '_castNumbers';
}
if ($this->_settings['changeTime']) {
$valueMethods[] = '_changeDateToUnix';
}
$this->_settings['keyMethods'] = array_merge($keyMethods, $this->_settings['keyMethods']);
$this->_settings['valueMethods'] = array_merge($valueMethods, $this->_settings['valueMethods']);
}
/**
* Calls a method. Optimizes where possible because of the
* large number of calls through this method.
*
* @param string|Closure|array $method
* @param mixed $variable
* @param mixed $key
* @return mixed
*/
protected function _call($method, &$variable, $key) {
if (is_string($method) && method_exists($this, $method)) {
return $this->$method($variable, $key);
}
if ($method instanceof Closure) {
return $method($variable, $key);
}
return call_user_func($method, $variable, $key);
}
/**
* Recurse through an array and apply key changes and casts.
*
* @param mixed $variable
* @param mixed $key
* @return void
*/
protected function _recurse(&$variable, $key = null) {
if (is_array($variable)) {
foreach ($this->_settings['keyMethods'] as $method) {
$variable = $this->_call($method, $variable, $key);
}
foreach ($variable as $k => &$value) {
$this->_recurse($value, $key === null ? $k : "$key.$k");
}
return;
}
foreach ($this->_settings['valueMethods'] as $method) {
$variable = $this->_call($method, $variable, $key);
}
}
/**
* Nests the secondary models in the array of the
* primary model.
*
* Might overwrite array keys if model field names have the
* same name as the secondary model.
*
* @param array $record
* @param string $primaryAlias
* @return array
*/
protected function _changeNesting(array $record, $primaryAlias) {
$new = $record[$primaryAlias];
unset($record[$primaryAlias]);
$new += $record;
return $new;
}
/**
* Replaces array keys for associated records.
*
* Might overwrite array keys if model field names have the
* same name as the secondary model.
*
* Example
* =======
*
* Replacing the array keys for the following associations:
*
* User hasMany Comment
* Comment belongsTo Post
*
* The array keys that will replaced:
*
* Comment -> comments (plural)
* Post -> post (singular)
*
* @param array $variable
* @param string|integer $key
* @param mixed $value
* @return void
*/
protected function _replaceKeys(array $variable) {
if (empty($this->_settings['replaceMap'])) {
$this->_settings['replaceMap'] = $this->_getReplaceMapFromAssociations();
}
$keys = array_keys($variable);
$replaced = false;
foreach ($keys as &$key) {
if (!is_string($key) || !is_array($variable[$key])) {
continue;
}
if (!isset($this->_settings['replaceMap'][$key])) {
continue;
}
$key = $this->_settings['replaceMap'][$key];
$replaced = true;
}
if (!$replaced) {
return $variable;
}
return array_combine($keys, array_values($variable));
}
/**
* Get a key-value map with replacements for the model keys.
* The replacements are derived from the associations.
*
* @param Model $model
* @param array $map
* @return boolean|array
*/
protected function _getReplaceMapFromAssociations(Model $model = null, array $map = null) {
if ($model === null) {
$model = $this->_model();
}
if ($map === null) {
$map = array($model->alias => Inflector::singularize(Inflector::tableize($model->alias)));
}
foreach ($model->associations() as $type) {
foreach ($model->{$type} as $alias => &$association) {
if (isset($map[$alias]) || !property_exists($model, $alias)) {
continue;
}
$key = Inflector::tableize($alias);
if ($type === 'belongsTo' || $type === 'hasOne') {
$key = Inflector::singularize($key);
}
$map[$alias] = $key;
$map = $this->_getReplaceMapFromAssociations($model->{$alias}, $map);
}
}
return $map;
}
/**
* Change "1" to 1, and "123.456" to 123.456.
*
* @param mixed $variable
* @return void
*/
protected function _castNumbers($variable) {
if (!is_numeric($variable)) {
return $variable;
}
return $variable + 0;
}
/**
* Converts database dates to unix times.
*
* @param mixed $variable
* @return integer
*/
protected function _changeDateToUnix($variable) {
if (!is_string($variable)) {
return $variable;
}
if (!preg_match('@^\d{4}-\d{2}-\d{2}@', $variable)) {
return $variable;
}
return strtotime($variable);
}
}

View File

@ -1,157 +0,0 @@
<?php
App::uses('CakeEvent', 'Event');
App::uses('DebugTimer', 'DebugKit.Lib');
App::uses('CrudListener', 'Crud.Controller/Crud');
/**
* Implements timings for DebugKit / Crud
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class DebugKitListener extends CrudListener {
/**
* List of events implemented by this class
*
* @return array
*/
public function implementedEvents() {
return array(
'Crud.startup' => array('callable' => 'startup'),
'Crud.beforeHandle' => array('callable' => 'beforeHandle', 'priority' => 1),
'Crud.beforeRender' => array('callable' => 'beforeRender', 'priority' => 5000),
'Crud.beforePaginate' => array('callable' => 'beforePaginate', 'priority' => 1),
'Crud.afterPaginate' => array('callable' => 'afterPaginate', 'priority' => 5000),
'Crud.beforeSave' => array('callable' => 'beforeSave', 'priority' => 1),
'Crud.afterSave' => array('callable' => 'afterSave', 'priority' => 5000),
'Crud.beforeFind' => array('callable' => 'beforeFind', 'priority' => 1),
'Crud.afterFind' => array('callable' => 'afterFind', 'priority' => 5000),
'Crud.beforeDelete' => array('callable' => 'beforeDelete', 'priority' => 1),
'Crud.afterDelete' => array('callable' => 'afterDelete', 'priority' => 5000),
);
}
/**
* Start timer for Crud.beforeHandle
*
* And enable event logging. The Crud.startup event will not itself have been logged
*
* @param CakeEvent $event
* @return void
*/
public function startup(CakeEvent $event) {
$this->_crud()->config('eventLogging', true);
$this->_crud()->logEvent('Crud.startup');
}
/**
* Start timer for Crud.init
*
* And enable event logging. The Crud.initialize event will not itself have been logged
*
* @param CakeEvent $event
* @return void
*/
public function beforeHandle(CakeEvent $event) {
parent::beforeHandle($event);
DebugTimer::start('Event: Crud.beforeHandle');
}
/**
* Stop timer for Crud.init
*
* @param CakeEvent $event
* @return void
*/
public function beforeRender(CakeEvent $event) {
DebugTimer::stop('Event: Crud.beforeHandle');
}
/**
* Start timer for Crud.Paginate
*
* @param CakeEvent $event
* @return void
*/
public function beforePaginate(CakeEvent $event) {
DebugTimer::start('Event: Crud.Paginate');
}
/**
* Stop timer for Crud.Paginate
*
* @param CakeEvent $event
* @return void
*/
public function afterPaginate(CakeEvent $event) {
DebugTimer::stop('Event: Crud.Paginate');
}
/**
* Start timer for Crud.Save
*
* @param CakeEvent $event
* @return void
*/
public function beforeSave(CakeEvent $event) {
DebugTimer::start('Event: Crud.Save');
}
/**
* Stop timer for Crud.Save
*
* @param CakeEvent $event
* @return void
*/
public function afterSave(CakeEvent $event) {
DebugTimer::stop('Event: Crud.Save');
}
/**
* Start timer for Crud.Find
*
* @param CakeEvent $event
* @return void
*/
public function beforeFind(CakeEvent $event) {
DebugTimer::start('Event: Crud.Find');
}
/**
* Stop timer for Crud.Find
*
* @param CakeEvent $event
* @return void
*/
public function afterFind(CakeEvent $event) {
DebugTimer::stop('Event: Crud.Find');
}
/**
* Start timer for Crud.Delete
*
* @param CakeEvent $event
* @return void
*/
public function beforeDelete(CakeEvent $event) {
DebugTimer::start('Event: Crud.Delete');
}
/**
* Stop timer for Crud.Delete
*
* @param CakeEvent $event
* @return void
*/
public function afterDelete(CakeEvent $event) {
DebugTimer::stop('Event: Crud.Delete');
}
}

View File

@ -1,176 +0,0 @@
<?php
App::uses('CakeEvent', 'Event');
App::uses('CrudListener', 'Crud.Controller/Crud');
/**
* Redirect Listener
*
* Listener to improve upon the default redirection behavior of Crud actions
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class RedirectListener extends CrudListener {
/**
* Settings
*
* @var array
*/
protected $_settings = array(
'readers' => array()
);
/**
* Returns a list of all events that will fire in the controller during its lifecycle.
* You can override this function to add your own listener callbacks
*
* @return array
*/
public function implementedEvents() {
return array(
'Crud.beforeRedirect' => array('callable' => 'beforeRedirect', 'priority' => 90)
);
}
/**
* Setup method
*
* Called when the listener is initialized
*
* Setup the default readers
*
* @return void
*/
public function setup() {
$this->reader('request.key', function(CrudSubject $subject, $key = null) {
if (!isset($subject->request->{$key})) {
return null;
}
return $subject->request->{$key};
});
$this->reader('request.data', function(CrudSubject $subject, $key = null) {
return $subject->request->data($key);
});
$this->reader('request.query', function(CrudSubject $subject, $key = null) {
return $subject->request->query($key);
});
$this->reader('model.key', function(CrudSubject $subject, $key = null) {
if (!isset($subject->model->{$key})) {
return null;
}
return $subject->model->{$key};
});
$this->reader('model.data', function(CrudSubject $subject, $key = null) {
return Hash::get($subject->model->data, $key);
});
$this->reader('model.field', function(CrudSubject $subject, $key = null) {
return $subject->model->field($key);
});
$this->reader('subject.key', function(CrudSubject $subject, $key = null) {
if (!isset($subject->{$key})) {
return null;
}
return $subject->{$key};
});
}
/**
* Add or replace a reader
*
* @param string $key
* @param mixed $reader
* @return mixed
*/
public function reader($key, $reader = null) {
if ($reader === null) {
return $this->config('readers.' . $key);
}
return $this->config('readers.' . $key, $reader);
}
/**
* Redirect callback
*
* If a special redirect key is provided, change the
* redirection URL target
*
* @param CakeEvent $event
* @return void
*/
public function beforeRedirect(CakeEvent $event) {
$subject = $event->subject;
$redirects = $this->_action()->redirectConfig();
if (empty($redirects)) {
return;
}
foreach ($redirects as $redirect) {
if (!$this->_getKey($subject, $redirect['reader'], $redirect['key'])) {
continue;
}
$subject->url = $this->_getUrl($subject, $redirect['url']);
break;
}
}
/**
* Get the new redirect URL
*
* Expand configurations where possible and replace the
* placeholder with the actual value
*
* @param CrudSubject $subject
* @param array $config
* @return array
*/
protected function _getUrl(CrudSubject $subject, array $url) {
foreach ($url as $key => $value) {
if (!is_array($value)) {
continue;
}
if ($key === '?') {
$url[$key] = $this->_getUrl($subject, $value);
continue;
}
$url[$key] = $this->_getKey($subject, $value[0], $value[1]);
}
return $url;
}
/**
* Return the value of `$type` with `$key`
*
* @throws Exception if the reader is invalid
* @param CrudSubject $subject
* @param string $reader
* @param string $key
* @return mixed
*/
protected function _getKey(CrudSubject $subject, $reader, $key) {
$callable = $this->reader($reader);
if ($callable === null || !is_callable($callable)) {
throw new Exception('Invalid reader: ' . $reader);
}
return $callable($subject, $key);
}
}

View File

@ -1,210 +0,0 @@
<?php
App::uses('CakeEventListener', 'Event');
App::uses('CrudListener', 'Crud.Controller/Crud');
/**
* Implements beforeRender event listener to set related models' lists to
* the view
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class RelatedModelsListener extends CrudListener {
/**
* Gets the list of associated model lists to be fetched for an action
*
* @param string $action name of the action
* @return array
*/
public function models($action = null) {
$settings = $this->_action($action)->relatedModels();
if ($settings === true) {
$ModelInstance = $this->_model();
return array_merge(
$ModelInstance->getAssociated('belongsTo'),
$ModelInstance->getAssociated('hasAndBelongsToMany')
);
}
if (empty($settings)) {
return array();
}
if (is_string($settings)) {
$settings = array($settings);
}
return $settings;
}
/**
* Find and publish all related models to the view
* for an action
*
* @param NULL|string $action If NULL the current action will be used
* @return void
*/
public function publishRelatedModels($action = null) {
$models = $this->models($action);
if (empty($models)) {
return;
}
$Controller = $this->_controller();
foreach ($models as $modelName) {
$associationType = $this->_getAssociationType($modelName);
$associatedModel = $this->_getModelInstance($modelName, $associationType);
$viewVar = Inflector::variable(Inflector::pluralize($associatedModel->alias));
if (array_key_exists($viewVar, $Controller->viewVars)) {
continue;
}
$query = $this->_getBaseQuery($associatedModel, $associationType);
$subject = $this->_trigger('beforeRelatedModel', compact('modelName', 'query', 'viewVar', 'associationType', 'associatedModel'));
$items = $this->_findRelatedItems($associatedModel, $subject->query);
$subject = $this->_trigger('afterRelatedModel', compact('modelName', 'items', 'viewVar', 'associationType', 'associatedModel'));
$Controller->set($subject->viewVar, $subject->items);
}
}
/**
* Fetches related models' list and sets them to a variable for the view
*
* @codeCoverageIgnore
* @param CakeEvent $event
* @return void
*/
public function beforeRender(CakeEvent $event) {
$this->publishRelatedModels();
}
/**
* Execute the DB query to find the related items
*
* @param Model $Model
* @param array $query
* @return array
*/
protected function _findRelatedItems(Model $Model, $query) {
if ($this->_hasTreeBehavior($Model)) {
return $Model->generateTreeList(
$query['conditions'],
$query['keyPath'],
$query['valuePath'],
$query['spacer'],
$query['recursive']
);
}
return $Model->find('list', $query);
}
/**
* Get the base query to find the related items for an associated model
*
* @param Model $associatedModel
* @param string $associationType
* @return array
*/
protected function _getBaseQuery(Model $associatedModel, $associationType = null) {
$query = array();
if ($associationType === 'belongsTo') {
$PrimaryModel = $this->_model();
$query['conditions'][] = $PrimaryModel->belongsTo[$associatedModel->alias]['conditions'];
}
if ($this->_hasTreeBehavior($associatedModel)) {
$TreeBehavior = $this->_getTreeBehavior($associatedModel);
$query = array(
'keyPath' => null,
'valuePath' => null,
'spacer' => '_',
'recursive' => $TreeBehavior->settings[$associatedModel->alias]['recursive']
);
if (empty($query['conditions'])) {
$query['conditions'][] = $TreeBehavior->settings[$associatedModel->alias]['scope'];
}
}
return $query;
}
/**
* Returns model instance based on its name
*
* @param string $modelName
* @param string $associationType
* @return Model
*/
protected function _getModelInstance($modelName, $associationType = null) {
$PrimaryModel = $this->_model();
if (isset($PrimaryModel->{$modelName})) {
return $PrimaryModel->{$modelName};
}
$Controller = $this->_controller();
if (isset($Controller->{$modelName}) && $Controller->{$modelName} instanceOf Model) {
return $Controller->{$modelName};
}
if ($associationType && !empty($PrimaryModel->{$associationType}[$modelName]['className'])) {
return $this->_classRegistryInit($PrimaryModel->{$associationType}[$modelName]['className']);
}
return $this->_classRegistryInit($modelName);
}
/**
* Returns model's association type with controller's model
*
* @param string $modelName
* @return string|null Association type if found else null
*/
protected function _getAssociationType($modelName) {
$associated = $this->_model()->getAssociated();
return isset($associated[$modelName]) ? $associated[$modelName] : null;
}
/**
* Check if a model has the Tree behavior attached or not
*
* @codeCoverageIgnore
* @param Model $Model
* @return boolean
*/
protected function _hasTreeBehavior(Model $Model) {
return $Model->Behaviors->attached('Tree');
}
/**
* Get the TreeBehavior from a model
*
* @codeCoverageIgnore
* @param Model $Model
* @return TreeBehavior
*/
protected function _getTreeBehavior(Model $Model) {
return $Model->Behaviors->Tree;
}
/**
* Wrapper for ClassRegistry::init for easier testing
*
* @codeCoverageIgnore
* @return Model
*/
protected function _classRegistryInit($modelName) {
return ClassRegistry::init($modelName);
}
}

View File

@ -1,159 +0,0 @@
<?php
App::uses('CakeEvent', 'Event');
App::uses('CrudListener', 'Crud.Controller/Crud');
/**
* Implements beforeRender event listener that uses the build-in
* scaffolding views in cakephp.
*
* Using this listener you don't have to bake your views when
* doing rapid prototyping of your application
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class ScaffoldListener extends CrudListener {
/**
* List of events implemented by this class
*
* @return array
*/
public function implementedEvents() {
return array(
'Crud.beforeRender' => 'beforeRender',
'Crud.beforeFind' => 'beforeFind',
'Crud.beforePaginate' => 'beforePaginate'
);
}
/**
* Make sure to contain associated models
*
* This have no effect on clean applications where containable isn't
* loaded, but for those who does have it loaded, we should
* use it.
*
* This help applications with `$recursive -1` in their AppModel
* and containable behavior loaded
*
* @param CakeEvent $event
* @return void
*/
public function beforeFind(CakeEvent $event) {
if (!isset($event->subject->query['contain'])) {
$event->subject->query['contain'] = array();
}
$existing = $event->subject->query['contain'];
$associated = array_keys($this->_model()->getAssociated());
$event->subject->query['contain'] = array_merge($existing, $associated);
}
/**
* Make sure to contain associated models
*
* This have no effect on clean applications where containable isn't
* loaded, but for those who does have it loaded, we should
* use it.
*
* This help applications with `$recursive -1` in their AppModel
* and containable behavior loaded
*
* @param CakeEvent $event
* @return void
*/
public function beforePaginate(CakeEvent $event) {
$Paginator = $this->_controller()->Paginator;
if (!isset($Paginator->settings['contain'])) {
$Paginator->settings['contain'] = array();
}
$existing = $Paginator->settings['contain'];
$associated = array_keys($this->_model()->getAssociated());
$Paginator->settings['contain'] = array_merge($existing, $associated);
}
/**
* Do all the magic needed for using the
* cakephp scaffold views
*
* @param CakeEvent
* @return void
*/
public function beforeRender(CakeEvent $event) {
$model = $this->_model();
$request = $this->_request();
$controller = $this->_controller();
$scaffoldTitle = Inflector::humanize(Inflector::underscore($controller->viewPath));
$title = __d('cake', 'Scaffold :: ') . Inflector::humanize($request->action) . ' :: ' . $scaffoldTitle;
$modelClass = $controller->modelClass;
$primaryKey = $model->primaryKey;
$displayField = $model->displayField;
$singularVar = Inflector::variable($modelClass);
$pluralVar = Inflector::variable($controller->name);
$singularHumanName = Inflector::humanize(Inflector::underscore($modelClass));
$pluralHumanName = Inflector::humanize(Inflector::underscore($controller->name));
$scaffoldFields = array_keys($model->schema());
$associations = $this->_associations($model);
$controller->set(compact(
'modelClass', 'primaryKey', 'displayField', 'singularVar', 'pluralVar',
'singularHumanName', 'pluralHumanName', 'scaffoldFields', 'associations'
));
$controller->set('title_for_layout', $title);
if ($controller->viewClass) {
$controller->viewClass = 'Scaffold';
}
}
/**
* Returns associations for controllers models.
*
* @param Model $model
* @return array Associations for model
*/
protected function _associations(Model $model) {
$associations = array();
$associated = $model->getAssociated();
foreach ($associated as $assocKey => $type) {
if (!isset($associations[$type])) {
$associations[$type] = array();
}
$assocDataAll = $model->$type;
$assocData = $assocDataAll[$assocKey];
$associatedModel = $model->{$assocKey};
$associations[$type][$assocKey]['primaryKey'] = $associatedModel->primaryKey;
$associations[$type][$assocKey]['displayField'] = $associatedModel->displayField;
$associations[$type][$assocKey]['foreignKey'] = $assocData['foreignKey'];
list($plugin, $modelClass) = pluginSplit($assocData['className']);
if ($plugin) {
$plugin = Inflector::underscore($plugin);
}
$associations[$type][$assocKey]['plugin'] = $plugin;
$associations[$type][$assocKey]['controller'] = Inflector::pluralize(Inflector::underscore($modelClass));
if ($type === 'hasAndBelongsToMany') {
$associations[$type][$assocKey]['with'] = $assocData['with'];
}
}
return $associations;
}
}

View File

@ -1,211 +0,0 @@
<?php
App::uses('CrudListener', 'Crud.Controller/Crud');
/**
* Search Listener
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class SearchListener extends CrudListener {
/**
* Default configuration
*
* @var array
*/
protected $_settings = array(
'component' => array(
'commonProcess' => array(
'paramType' => 'querystring'
),
'presetForm' => array(
'paramType' => 'querystring'
)
),
'scope' => array()
);
/**
* Returns a list of all events that will fire in the controller during its lifecycle.
* You can override this function to add you own listener callbacks
*
* We attach at priority 50 so normal bound events can run before us
*
* @return array
*/
public function implementedEvents() {
return array(
'Crud.beforeHandle' => array('callable' => 'beforeHandle', 'priority' => 50),
'Crud.beforePaginate' => array('callable' => 'beforePaginate', 'priority' => 50)
);
}
public function beforeHandle(CakeEvent $event) {
$request = $this->_request();
$model = $this->_model();
if (!array_key_exists($model->alias, $request->data)) {
return;
}
if (!array_key_exists('_search', $request->data($model->alias))) {
return;
}
$controller = $this->_controller();
$this->_ensureComponent($controller);
$this->_ensureBehavior($model);
$this->_commonProcess($controller, $model->name);
}
/**
* Define a new scope
*
* @param string $name Name of the scope (?scope=$name)
* @param array $query The query arguments to pass to Search
* @param array|null $filter The filterArgs to use on the model
* @return ScopedListener
*/
public function scope($name, $query, $filter = null) {
$this->config('scope.' . $name, compact('query', 'filter'));
return $this;
}
/**
* beforePaginate callback
*
* @param CakeEvent $e
* @return void
*/
public function beforePaginate(CakeEvent $e) {
$this->_checkRequiredPlugin();
$model = $this->_model();
$controller = $this->_controller();
$request = $this->_request();
$this->_ensureComponent($controller);
$this->_ensureBehavior($model);
$this->_commonProcess($controller, $model->name);
$query = $request->query;
if (!empty($request->query['_scope'])) {
$config = $this->config('scope.' . $request->query['_scope']);
if (empty($config)) {
$config = $this->_action()->config('scope.' . $request->query['_scope']);
}
$query = Hash::get((array)$config, 'query');
if (!empty($config['filter'])) {
$this->_setFilterArgs($model, $config['filter']);
}
} else {
$filterArgs = $this->_action()->config('scope');
if (!empty($filterArgs)) {
$this->_setFilterArgs($model, (array)$filterArgs);
}
}
// Avoid notice if there is no filterArgs
if (empty($model->filterArgs)) {
$this->_setFilterArgs($model, array());
}
$this->_setPaginationOptions($controller, $model, $query);
}
/**
* Check that the cakedc/search plugin is installed
*
* @throws CakeException If cakedc/search isn't loaded
* @return void
*/
protected function _checkRequiredPlugin() {
if (CakePlugin::loaded('Search')) {
return;
}
throw new CakeException('SearchListener requires the CakeDC/search plugin. Please install it from https://github.com/CakeDC/search');
}
/**
* Ensure that the Prg component is loaded from
* the Search plugin
*
* @param Controller $controller
* @return void
*/
protected function _ensureComponent(Controller $controller) {
if ($controller->Components->loaded('Prg')) {
return;
}
$controller->Prg = $controller->Components->load('Search.Prg', $this->config('component'));
$controller->Prg->initialize($controller);
$controller->Prg->startup($controller);
}
/**
* Ensure that the searchable behavior is loaded
*
* @param Model $model
* @return void
*/
protected function _ensureBehavior(Model $model) {
if ($model->Behaviors->loaded('Searchable')) {
return;
}
$model->Behaviors->load('Search.Searchable');
$model->Behaviors->Searchable->setup($model);
}
/**
* Execute commonProcess on Prg component
*
* @codeCoverageIgnore
* @param Controller $controller
* @param string $modelClass
* @return void
*/
protected function _commonProcess(Controller $controller, $modelClass) {
$controller->Prg->commonProcess($modelClass);
}
/**
* Set the pagination options
*
* @codeCoverageIgnore
* @param Controller $controller
* @param Model $model
* @param array $query
* @return void
*/
protected function _setPaginationOptions(Controller $controller, Model $model, $query) {
if (!isset($controller->Paginator->settings['conditions'])) {
$controller->Paginator->settings['conditions'] = array();
}
$controller->Paginator->settings['conditions'] = array_merge(
$controller->Paginator->settings['conditions'],
$model->parseCriteria($query)
);
}
/**
* Set the model filter args
*
* @codeCoverageIgnore
* @param Model $model
* @param array $filter
* @return void
*/
protected function _setFilterArgs(Model $model, $filter) {
$model->filterArgs = $filter;
$model->Behaviors->Searchable->setup($model);
}
}

View File

@ -1,132 +0,0 @@
<?php
App::uses('ExceptionRenderer', 'Error');
/**
* Exception renderer for ApiListener
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class CrudExceptionRenderer extends ExceptionRenderer {
/**
* Renders validation errors and sends a 412 error code
*
* @param ValidationErrorException $error
* @return void
*/
public function crudValidation($error) {
$url = $this->controller->request->here();
$status = $code = $error->getCode();
try {
$this->controller->response->statusCode($status);
} catch(Exception $e) {
$status = 412;
$this->controller->response->statusCode($status);
}
$sets = array(
'code' => $code,
'url' => h($url),
'name' => $error->getMessage(),
'error' => $error,
'errorCount' => $error->getValidationErrorCount(),
'errors' => $error->getValidationErrors(),
'_serialize' => array('code', 'url', 'name', 'errorCount', 'errors')
);
$this->controller->set($sets);
$this->_outputMessage('error400');
}
/**
* Generate the response using the controller object.
*
* If there is no specific template for the raised error (normally there won't be one)
* swallow the missing view exception and just use the standard
* error format. This prevents throwing an unknown Exception and seeing instead
* a MissingView exception
*
* @param string $template The template to render.
* @return void
*/
protected function _outputMessage($template) {
try {
$this->controller->set('success', false);
$this->controller->set('data', $this->_getErrorData());
$this->controller->set('_serialize', array('success', 'data'));
$this->controller->render($template);
$this->controller->afterFilter();
$this->controller->response->send();
} catch (MissingViewException $e) {
$this->_outputMessageSafe('error500');
} catch (Exception $e) {
$this->controller->set(array(
'error' => $e,
'name' => $e->getMessage(),
'code' => $e->getCode()
));
$this->_outputMessageSafe('error500');
}
}
/**
* A safer way to render error messages, replaces all helpers, with basics
* and doesn't call component methods.
*
* @param string $template The template to render
* @return void
*/
protected function _outputMessageSafe($template) {
$this->controller->layoutPath = '';
$this->controller->subDir = '';
$this->controller->viewPath = 'Errors/';
$this->controller->viewClass = 'View';
$this->controller->helpers = array('Form', 'Html', 'Session');
$this->controller->render($template);
$this->controller->response->send();
}
/**
* Helper method used to generate extra debugging data into the error template
*
* @return array debugging data
*/
protected function _getErrorData() {
$data = array();
$viewVars = $this->controller->viewVars;
if (!empty($viewVars['_serialize'])) {
foreach ($viewVars['_serialize'] as $v) {
$data[$v] = $viewVars[$v];
}
}
if (!empty($viewVars['error'])) {
$data['exception'] = array(
'class' => get_class($viewVars['error']),
'code' => $viewVars['error']->getCode(),
'message' => $viewVars['error']->getMessage()
);
}
if (Configure::read('debug')) {
$data['exception']['trace'] = preg_split('@\n@', $viewVars['error']->getTraceAsString());
}
if (class_exists('ConnectionManager') && Configure::read('debug') > 1) {
$sources = ConnectionManager::sourceList();
$data['queryLog'] = array();
foreach ($sources as $source) {
$db = ConnectionManager::getDataSource($source);
if (!method_exists($db, 'getLog')) {
continue;
}
$data['queryLog'][$source] = $db->getLog(false, false);
}
}
return $data;
}
}

View File

@ -1,98 +0,0 @@
<?php
/**
* Exception containing validation errors from the model. Useful for API
* responses where you need an error code in response
*
*/
class CrudValidationException extends CakeException {
/**
* List of validation errors that occurred in the model
*
* @var array
*/
protected $_validationErrors = array();
/**
* How many validation errors are there?
*
* @var integer
*/
protected $_validationErrorCount = 0;
/**
* Constructor
*
* @param array $error list of validation errors
* @param integer $code code to report to client
* @return void
*/
public function __construct($errors, $code = 412) {
$this->_validationErrors = array_filter($errors);
$flat = Hash::flatten($this->_validationErrors);
$errorCount = $this->_validationErrorCount = count($flat);
$this->message = __dn('crud', 'A validation error occurred', '%d validation errors occurred', $errorCount, array($errorCount));
if ($errorCount === 1) {
$code = $this->_deriveRuleSpecific($this->_validationErrors, $code);
}
parent::__construct($this->message, $code);
}
/**
* _deriveRuleSpecific
*
* If there is only one error, change the exception message to be rule specific
* Also change the response code to be that of the validation rule if defined
*
* @param array $errors
* @param integer $code
* @return integer
*/
protected function _deriveRuleSpecific($errors = array(), $code = 412) {
$model = key($errors);
$field = key($errors[$model]);
$error = $errors[$model][$field][0];
$instance = ClassRegistry::getObject($model);
if (!isset($instance->validate[$field])) {
return $code;
}
foreach ($instance->validate[$field] as $key => $rule) {
$matchesMessage = (isset($rule['message']) && $error === $rule['message']);
if ($key !== $error && !$matchesMessage) {
continue;
}
$this->message = sprintf('%s.%s : %s', $model, $field, $error);
if (!empty($rule['code'])) {
$code = $rule['code'];
}
break;
}
return $code;
}
/**
* Returns the list of validation errors
*
* @return array
*/
public function getValidationErrors() {
return $this->_validationErrors;
}
/**
* How many validation errors are there?
*
* @return integer
*/
public function getValidationErrorCount() {
return $this->_validationErrorCount;
}
}

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2013 Christian "Jippi" Winther
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,64 +0,0 @@
<?php
/**
* Enable Crud to catch MissingActionException and attempt to generate response
* using Crud.
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
trait CrudControllerTrait {
/**
* List of components that are capable of dispatching an action that is
* not already implemented
*
* @var string
*/
public $dispatchComponents = array();
/**
* Dispatches the controller action. Checks that the action exists and isn't private.
*
* If CakePHP raises MissingActionException we attempt to execute Crud
*
* @param CakeRequest $request
* @return mixed The resulting response.
* @throws PrivateActionException When actions are not public or prefixed by _
* @throws MissingActionException When actions are not defined and scaffolding and CRUD is not enabled.
*/
public function invokeAction(CakeRequest $request) {
try {
return parent::invokeAction($request);
} catch (MissingActionException $e) {
if (!empty($this->dispatchComponents)) {
foreach ($this->dispatchComponents as $component => $enabled) {
if (empty($enabled)) {
continue;
}
// Skip if isActionMapped isn't defined in the Component
if (!method_exists($this->{$component}, 'isActionMapped')) {
continue;
}
// Skip if the action isn't mapped
if (!$this->{$component}->isActionMapped($request->action)) {
continue;
}
// Skip if execute isn't defined in the Component
if (!method_exists($this->{$component}, 'execute')) {
continue;
}
// Execute the callback, should return CakeResponse object
return $this->{$component}->execute();
}
}
// No additional callbacks, re-throw the normal CakePHP exception
throw $e;
}
}
}

View File

@ -1,125 +0,0 @@
<?php
App::uses('Debugger', 'Utility');
App::uses('DebugPanel', 'DebugKit.Lib');
/**
* Crud debug panel in DebugKit
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class CrudPanel extends DebugPanel {
/**
* Declare we are a plugin
*
* @var string
*/
public $plugin = 'Crud';
/**
* beforeRender callback
*
* @param Controller $controller
* @return void
*/
public function beforeRender(Controller $controller) {
$component = $controller->Crud->config();
if ($controller->Crud->isActionMapped()) {
$Action = $controller->Crud->action();
$action = $Action->config();
}
$eventManager = $controller->getEventManager();
$eventLog = $controller->Crud->eventLog();
$events = array();
foreach ($eventLog as $event) {
list($name, $data) = $event;
$listeners = $eventManager->listeners($name);
$callbacks = $this->_getCallbacks($listeners);
$uName = $this->_getUniqueName($name, $events);
$events[$uName] = array(
'data' => $data,
'callbacks' => $callbacks
);
}
$listeners = array();
foreach ($controller->Crud->config('listeners') as $listener => $value) {
$listeners[$listener] = $controller->Crud->listener($listener)->config();
}
$controller->set('crudDebugKitData', compact('component', 'action', 'events', 'listeners'));
}
/**
* _getCallbacks
*
* Return all callbacks for a given event key
*
* @param array $listeners
* @return array
*/
protected function _getCallbacks($listeners) {
foreach ($listeners as &$listener) {
$listener = $listener['callable'];
if (is_array($listener)) {
$class = is_string($listener[0]) ? $listener[0] : get_class($listener[0]);
$method = $listener[1];
$listener = "$class::$method";
} elseif ($listener instanceof Closure) {
$listener = $this->_getClosureDefinition($listener);
}
}
return $listeners;
}
/**
* Return where a closure has been defined
*
* If for some reason this doesn't work - it'll return the closure instance in the full knowledge
* that it'll probably get dumped as the string "function"
*
* @param Closure $closure
* @return mixed string or Closure
*/
protected function _getClosureDefinition(Closure $closure) {
$exported = ReflectionFunction::export($closure, true);
preg_match('#@@ (.*) (\d+) - (\d+)#', $exported, $match);
if (!$match) {
return $closure;
}
list($m, $path, $start) = $match;
$path = Debugger::trimPath($path);
return "$path:$start";
}
/**
* _getUniqueName
*
* The name is used as an array key, ensure there are no collisions by adding a numerical
* suffix if the given name already exists
*
* @param string $name
* @param array $existing
* @return string
*/
protected function _getUniqueName($name, $existing) {
$count = 1;
$suffix = '';
while (isset($existing[$name . $suffix])) {
$suffix = ' #' . ++$count;
}
return $name . $suffix;
}
}

View File

@ -1,59 +0,0 @@
[![Stories in Ready](https://badge.waffle.io/friendsofcake/crud.png?label=ready)](https://waffle.io/friendsofcake/crud)
[![Build Status](https://travis-ci.org/FriendsOfCake/crud.png?branch=master)](https://travis-ci.org/FriendsOfCake/crud)
[![Coverage Status](https://coveralls.io/repos/FriendsOfCake/crud/badge.png?branch=master)](https://coveralls.io/r/FriendsOfCake/crud?branch=master)
[![Total Downloads](https://poser.pugx.org/FriendsOfCake/crud/d/total.png)](https://packagist.org/packages/FriendsOfCake/crud)
[![Latest Stable Version](https://poser.pugx.org/FriendsOfCake/crud/v/stable.png)](https://packagist.org/packages/FriendsOfCake/crud)
# Version notice
The master and develop branches only works for CakePHP 2.x - please use the [cake3 branch](https://github.com/FriendsOfCake/crud/tree/cake3) for CakePHP 3.x
# Introduction
Crud was built to be [scaffolding](http://book.cakephp.org/2.0/en/controllers/scaffolding.html) on
steroids, and allow developers to have enough flexibility to use it for both rapid prototyping and
production applications, even on the same code base -- saving you time.
* Crud is [very fast to install](http://friendsofcake.com/crud/docs/installation.html), a few minutes tops.
* Crud is very flexible and has tons of [configuration options](http://friendsofcake.com/crud/docs/configuration.html).
* Crud aims to stay out of your way, and if it happens to get in your way, you can change the undesired
behavior very easily.
* Crud relies heavily on CakePHP events and is possible to override, extend, or disable almost all
of Crud's functionality either globally or for one specific action.
* Usually, the basic code for controller CRUD actions are very simple and always looks the same. Crud
will add the actions to your controller so you don't have to reimplement them over and over again.
* Crud does not have the same limitations as CakePHP's own scaffolding, which is "my way or the
highway." Crud allows you to hook into all stages of a request, only building the controller code
needed specifically for your business logic, outsourcing all the heavy boilerplating to Crud.
* Less boilerplate code means less code to maintain, and less code to spend time unit testing.
* Crud allows you to use your own views, baked or hand-crafted, in addition to adding the
code needed to fulfill your application logic, using [events](http://friendsofcake.com/crud/docs/events.html). It is
by default compatible with CakePHP's baked views.
* Crud also provides built in features for JSON and XML [API](http://friendsofcake.com/crud/docs/listeners/api.html)
for any action you have enabled through Crud, which eliminates maintaining both a
HTML frontend and a JSON and/or XML interface for your applications -- saving you tons of time and
having a leaner code base.
# Bugs
If you happen to stumble upon a bug, please feel free to create a pull request with a fix
(optionally with a test), and a description of the bug and how it was resolved.
You can also create an issue with a description to raise awareness of the bug.
# Features
If you have a good idea for a Crud feature, please join us on IRC and let's discuss it. Pull
requests are always more than welcome.
# Support / Questions
You can join us on IRC in the #FriendsOfCake channel for any support or questions.

View File

@ -1,60 +0,0 @@
<?php
App::uses('DispatcherFilter', 'Routing');
/**
* Make HEAD requests be treated as GET requests
*
* This filter is intended to be used with the HttpMethod filter
*/
class FakeHeadFilter extends DispatcherFilter {
/**
* priority
*
* Run before any default-priority filters
*
* @var int
*/
public $priority = 9;
/**
* Is the request really a HEAD request?
*
* @var mixed
*/
protected $_isHead;
/**
* Make application see a GET request instead of a HEAD request
*
* If the application considers HEAD an unsupported method, this allows
* the application to see/treat them as GET requests
*
* @param CakeEvent $event
* @return CakeResponse|null
*/
public function beforeDispatch(CakeEvent $event) {
$request = $event->data['request'];
$this->_isHead = $request->is('head');
if ($this->_isHead) {
$this->_requestMethod = $request->method();
$_SERVER['REQUEST_METHOD'] = 'GET';
}
}
/**
* Rewrite the REQUEST_METHOD if it was a head request
*
* So that any subsequent dispatch filter logic knows it was a head request
*
* @param CakeEvent $event
* @return CakeResponse|null
*/
public function afterDispatch(CakeEvent $event) {
if ($this->_isHead) {
$_SERVER['REQUEST_METHOD'] = 'HEAD';
}
}
}

View File

@ -1,104 +0,0 @@
<?php
App::uses('DispatcherFilter', 'Routing');
/**
* HttpMethodFilter
*
* Automatically handle OPTIONS and HEAD requests
*/
class HttpMethodFilter extends DispatcherFilter {
/**
* priority
*
* Run last, allowing for another filter to add more headers to the response if necessary
* by running with a lower priority
*
* @var int
*/
public $priority = 11;
/**
* defaultVerbs
*
* The list of verbs to check, if not overwritten by calling:
*
* Configure::write('Crud.HttpMethodFilter.verbs', ['list', 'of', 'verbs']);
*
* @var array
*/
public $defaultVerbs = array(
'GET',
'HEAD',
'POST',
'PUT',
'DELETE',
);
/**
* Handle OPTIONS requests
*
* If it's an options request, loop on the configured http verbs and add
* an Access-Control-Allow-Methods header with the verbs the application
* is configured to respond to.
*
* @param CakeEvent $event
* @return CakeResponse|null
*/
public function beforeDispatch(CakeEvent $event) {
$request = $event->data['request'];
if (!$request->is('options')) {
return;
}
$event->stopPropagation();
$url = $request->url;
$verbs = Configure::read('Crud.HttpMethodFilter.verbs') ?: $this->defaultVerbs;
$allowedMethods = array();
foreach ($verbs as $verb) {
$_SERVER['REQUEST_METHOD'] = $verb;
if (Router::parse('/' . $url)) {
$allowedMethods[] = $verb;
}
}
$_SERVER['REQUEST_METHOD'] = 'OPTIONS';
$response = $event->data['response'];
$response->header('Access-Control-Allow-Methods', implode(', ', $allowedMethods));
return $response;
}
/**
* Handle HEAD requests
*
* A head request cannot have a body, if it hasn't been handled automatically it's
* assumed to have been handled as a GET request. Remove the body before responding,
* and add a content-length header
*
* @param CakeEvent $event
* @return CakeResponse|null
*/
public function afterDispatch(CakeEvent $event) {
$request = $event->data['request'];
if (!$request->is('head')) {
return;
}
$response = $event->data['response'];
$headers = $response->header();
$length = isset($headers['Content-length']) ? $headers['Content-length'] : null;
$bodyLength = strlen($response->body());
if ($length === null && $bodyLength) {
$response->header('Content-length', $bodyLength);
}
$response->body('');
return $response;
}
}

View File

@ -1,34 +0,0 @@
<?php
/**
* All Crud Test
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class AllCrudTest extends PHPUnit_Framework_TestSuite {
public static function suite() {
$suite = new CakeTestSuite('All Crud plugin tests');
$path = App::pluginPath('Crud');
$testPath = $path . DS . 'Test' . DS . 'Case';
if (!is_dir($testPath)) {
continue;
}
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($testPath), RecursiveIteratorIterator::CHILD_FIRST);
foreach ($iterator as $folder) {
$folder = (string)$folder;
$folderName = basename($folder);
if ($folderName === '.' || $folderName === '..') {
continue;
}
$suite->addTestDirectory($folder);
}
$suite->addTestDirectory($testPath);
return $suite;
}
}

View File

@ -1,449 +0,0 @@
<?php
App::uses('TranslationsShell', 'Crud.Console/Command');
App::uses('CakeRequest', 'Network');
App::uses('ConsoleInput', 'Console');
App::uses('ConsoleOutput', 'Console');
App::uses('Controller', 'Controller');
/**
* TranslationsShellTest
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class TranslationsShellTest extends CakeTestCase {
/**
* setup test
*
* @return void
*/
public function setUp() {
$this->out = $this->getMock('ConsoleOutput', array(), array(), '', false);
$this->in = $this->getMock('ConsoleInput', array(), array(), '', false);
$this->Shell = $this->getMock(
'TranslationsShell',
array('in', 'out', 'hr', 'err', '_stop', '_getControllers', '_loadController'),
array($this->out, $this->out, $this->in)
);
parent::setUp();
}
/**
* tearDown
*
* @return void
*/
public function tearDown() {
parent::tearDown();
CakePlugin::unload('TestPlugin');
}
/**
* testGenerateTranslations
*
* With no controllers, nothing's going to happen
*
* @return void
*/
public function testGenerateTranslations() {
$method = new ReflectionMethod('TranslationsShell', '_processController');
$method->setAccessible(true);
$method->invoke($this->Shell, false);
$expected = array();
$this->assertSame($expected, $this->Shell->lines);
}
/**
* testGenerateTranslationsForAModel
*
* @return void
*/
public function testGenerateTranslationsForAModel() {
$controller = new Controller(new CakeRequest());
$controller->Example = new StdClass(); // dummy
$controller->Example->name = 'Example';
$controller->modelClass = 'Example';
$controller->components = array(
'Crud.Crud' => array(
'actions' => array(
'index', 'add', 'edit', 'view', 'delete'
)
)
);
$controller->constructClasses();
$controller->startupProcess();
$this->Shell
->expects($this->once())
->method('_loadController')
->will($this->returnValue($controller));
$method = new ReflectionMethod('TranslationsShell', '_processController');
$method->setAccessible(true);
$method->invoke($this->Shell, 'Example');
$expected = array(
"",
"/**",
" * Example CRUD Component translations",
" */",
"__d('crud', 'Invalid id');",
"__d('crud', 'Not found');",
"__d('crud', 'Method not allowed. This action permits only {methods}');",
"__d('crud', 'Successfully created example');",
"__d('crud', 'Could not create example');",
"__d('crud', 'Successfully updated example');",
"__d('crud', 'Could not update example');",
"__d('crud', 'Successfully deleted example');",
"__d('crud', 'Could not delete example');"
);
$this->assertSame($expected, $this->Shell->lines);
}
/**
* testGenerateTranslationsForAModel
*
* @return void
*/
public function testGenerateTranslationsForAModelActionDomain() {
$controller = new Controller(new CakeRequest());
$controller->Example = new StdClass(); // dummy
$controller->Example->name = 'Example';
$controller->modelClass = 'Example';
$controller->components = array(
'Crud.Crud' => array(
'actions' => array(
'index', 'add', 'edit', 'view', 'delete'
)
)
);
$controller->constructClasses();
$controller->startupProcess();
$controller->Crud->config('messages.domain', 'my');
$this->Shell
->expects($this->once())
->method('_loadController')
->will($this->returnValue($controller));
$method = new ReflectionMethod('TranslationsShell', '_processController');
$method->setAccessible(true);
$method->invoke($this->Shell, 'Example');
$expected = array(
"",
"/**",
" * Example CRUD Component translations",
" */",
"__d('my', 'Invalid id');",
"__d('my', 'Not found');",
"__d('my', 'Method not allowed. This action permits only {methods}');",
"__d('my', 'Successfully created example');",
"__d('my', 'Could not create example');",
"__d('my', 'Successfully updated example');",
"__d('my', 'Could not update example');",
"__d('my', 'Successfully deleted example');",
"__d('my', 'Could not delete example');"
);
$this->assertSame($expected, $this->Shell->lines);
}
public function testGenerateFile() {
$controller = new Controller(new CakeRequest());
$controller->Example = new StdClass(); // dummy
$controller->Example->name = 'Example';
$controller->modelClass = 'Example';
$controller->components = array(
'Crud.Crud' => array(
'actions' => array(
'index', 'add', 'edit', 'view', 'delete'
)
)
);
$controller->constructClasses();
$controller->startupProcess();
$this->Shell
->expects($this->once())
->method('_loadController')
->will($this->returnValue($controller));
$this->Shell
->expects($this->once())
->method('_getControllers')
->will($this->returnValue(array('Example')));
$path = TMP . 'crud_translations_shell_test.php';
if (file_exists($path)) {
unlink($path);
}
$this->Shell->path($path);
$this->Shell->generate();
$this->assertFileExists($path);
$contents = file_get_contents($path);
$expected = <<<END
<?php
/**
* Example CRUD Component translations
*/
__d('crud', 'Invalid id');
__d('crud', 'Not found');
__d('crud', 'Method not allowed. This action permits only {methods}');
__d('crud', 'Successfully created example');
__d('crud', 'Could not create example');
__d('crud', 'Successfully updated example');
__d('crud', 'Could not update example');
__d('crud', 'Successfully deleted example');
__d('crud', 'Could not delete example');
END;
$this->assertTextEquals(trim($expected), trim($contents));
unlink($path);
}
/**
* testGenerateFileFileExists
*
* Running the shell should only add missing translations,
* Without removing or corrupting existing translations.
*
* @return void
*/
public function testGenerateFileFileExists() {
$expected = <<<END
<?php
/**
* Example CRUD Component translations
*/
__d('crud', 'Some other translation');
__d('crud', 'Invalid id');
__d('crud', 'Not found');
__d('crud', 'Method not allowed. This action permits only {methods}');
__d('crud', 'Successfully created example');
__d('crud', 'Could not create example');
__d('crud', 'Successfully updated example');
__d('crud', 'Could not update example');
__d('crud', 'Successfully deleted example');
END;
$path = TMP . 'crud_translations_shell_test.php';
file_put_contents($path, $expected);
$controller = new Controller(new CakeRequest());
$controller->Example = new StdClass(); // dummy
$controller->Example->name = 'Example';
$controller->modelClass = 'Example';
$controller->components = array(
'Crud.Crud' => array(
'actions' => array(
'index', 'add', 'edit', 'view', 'delete'
)
)
);
$controller->constructClasses();
$controller->startupProcess();
$this->Shell
->expects($this->once())
->method('_loadController')
->will($this->returnValue($controller));
$this->Shell
->expects($this->once())
->method('_getControllers')
->will($this->returnValue(array('Example')));
$this->Shell->path($path);
$this->Shell->generate();
$this->assertFileExists($path);
$onlyNewTranslation = "\n__d('crud', 'Could not delete example');";
$expected .= $onlyNewTranslation;
$contents = file_get_contents($path);
$this->assertTextEquals(trim($expected), trim($contents), "Only expected one translation to be added");
unlink($path);
}
/**
* testGetControllersDefault
*
* Verify that it returns a list of controller names without the Controller suffix
* When called with no args, should return the app controller names
*
* @return void
*/
public function testGetControllersDefault() {
$class = new ReflectionClass('TranslationsShell');
$method = $class->getMethod('_getControllers');
$method->setAccessible(true);
$this->Shell = $this->getMock(
'TranslationsShell',
array('in', 'out', 'hr', 'err', '_stop', '_loadController'),
array($this->out, $this->out, $this->in)
);
$path = CAKE . 'Test' . DS . 'test_app' . DS . 'Controller' . DS;
App::build(array(
'Controller' => array($path)
), App::RESET);
$expected = array(
'Pages',
'TestAppsError',
'TestConfigs',
'TestsApps',
'TestsAppsPosts'
);
if (!file_exists($path . 'TestConfigsController.php')) {
unset($expected[2]);
$expected = array_values($expected);
}
$controllers = $method->invoke($this->Shell);
$this->assertSame($expected, $controllers);
}
/**
* testGetControllersJunk
*
* @return void
*/
public function testGetControllersJunk() {
$class = new ReflectionClass('TranslationsShell');
$method = $class->getMethod('_getControllers');
$method->setAccessible(true);
$this->Shell = $this->getMock(
'TranslationsShell',
array('in', 'out', 'hr', 'err', '_stop', '_loadController'),
array($this->out, $this->out, $this->in)
);
$args = array(
'not a path'
);
$expected = array();
$controllers = $method->invoke($this->Shell, $args);
$this->assertSame($expected, $controllers);
}
/**
* testGetControllersAppNamed
*
* If the file paths to app controllers are passed - should be honored
*
* @return void
*/
public function testGetControllersAppNamed() {
$class = new ReflectionClass('TranslationsShell');
$method = $class->getMethod('_getControllers');
$method->setAccessible(true);
$this->Shell = $this->getMock(
'TranslationsShell',
array('in', 'out', 'hr', 'err', '_stop', '_loadController'),
array($this->out, $this->out, $this->in)
);
$args = array(
'Controller/ThisController.php',
'Controller/ThatController.php',
'Controller/OtherController.php',
);
$expected = array(
'This',
'That',
'Other'
);
$controllers = $method->invoke($this->Shell, $args);
$this->assertSame($expected, $controllers);
}
/**
* testGetControllersPlugin
*
* Passing the path to a plugin should process all controllers in that plugin
*
* @return void
*/
public function testGetControllersPlugin() {
$class = new ReflectionClass('TranslationsShell');
$method = $class->getMethod('_getControllers');
$method->setAccessible(true);
$this->Shell = $this->getMock(
'TranslationsShell',
array('in', 'out', 'hr', 'err', '_stop', '_loadController'),
array($this->out, $this->out, $this->in)
);
$this->Shell->args = array(
CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS . 'TestPlugin'
);
$path = CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS;
App::build(array(
'Plugin' => array($path)
), App::RESET);
CakePlugin::load('TestPlugin');
$expected = array(
'TestPlugin.TestPlugin',
'TestPlugin.Tests'
);
$controllers = $method->invoke($this->Shell, array($path . 'TestPlugin'));
$this->assertSame($expected, $controllers);
}
/**
* testGetControllersPluginNamed
*
* Passing the path to a single plugin controller should return that controller
*
* @return void
*/
public function testGetControllersPluginNamed() {
$class = new ReflectionClass('TranslationsShell');
$method = $class->getMethod('_getControllers');
$method->setAccessible(true);
$this->Shell = $this->getMock(
'TranslationsShell',
array('in', 'out', 'hr', 'err', '_stop', '_loadController'),
array($this->out, $this->out, $this->in)
);
$this->Shell->args = array(
CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS . 'TestPlugin'
);
$path = CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS;
App::build(array(
'Plugin' => array($path)
), App::RESET);
CakePlugin::load('TestPlugin');
$expected = array(
'TestPlugin.TestPlugin'
);
$path .= 'TestPlugin/Controller/TestPluginController.php';
$controllers = $method->invoke($this->Shell, array($path));
$this->assertSame($expected, $controllers);
}
}

View File

@ -1,58 +0,0 @@
<?php
App::uses('Model', 'Model');
/**
* Crud Example Model
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class CrudExample extends Model {
public $alias = 'CrudExample';
public $useTable = 'posts';
public $useDbConfig = 'test';
public $findMethods = array(
'published' => true,
'unpublished' => true,
'firstPublished' => true,
'firstUnpublished' => true,
);
protected function _findPublished($state, $query, $results = array()) {
if ($state === 'before') {
$query['conditions']['published'] = 'Y';
return $query;
}
return $results;
}
protected function _findUnpublished($state, $query, $results = array()) {
if ($state === 'before') {
$query['conditions']['published'] = 'N';
return $query;
}
return $results;
}
protected function _findFirstPublished($state, $query, $results = array()) {
if ($state === 'before') {
$query['conditions']['published'] = 'Y';
return parent::_findFirst($state, $query, $results);
}
return parent::_findFirst($state, $query, $results);
}
protected function _findFirstUnpublished($state, $query, $results = array()) {
if ($state === 'before') {
$query['conditions']['published'] = 'N';
return parent::_findFirst($state, $query, $results);
}
return parent::_findFirst($state, $query, $results);
}
}

View File

@ -1,306 +0,0 @@
<?php
App::uses('Model', 'Model');
App::uses('Controller', 'Controller');
App::uses('CakeRequest', 'Network');
App::uses('CakeResponse', 'Network');
App::uses('ComponentCollection', 'Controller');
App::uses('CrudTestCase', 'Crud.Test/Support');
App::uses('CrudSubject', 'Crud.Controller/Crud');
App::uses('AddCrudAction', 'Crud.Controller/Crud/Action');
App::uses('CrudComponent', 'Crud.Controller/Component');
App::uses('RedirectionListener', 'Crud.Controller/Crud/Listener');
/**
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class AddCrudActionTest extends CrudTestCase {
/**
* Test the normal HTTP GET flow of _get
*
* @covers AddCrudAction::_get
* @return void
*/
public function testActionGet() {
$Request = $this->getMock('CakeRequest');
$Model = $this->getMock('Model', array('create'));
$Model
->expects($this->once())
->method('create');
$Action = $this
->getMockBuilder('AddCrudAction')
->disableOriginalConstructor()
->setMethods(array('_request', '_model', '_trigger'))
->getMock();
$i = 0;
$Action
->expects($this->at($i++))
->method('_request')
->will($this->returnValue($Request));
$Action
->expects($this->at($i++))
->method('_model')
->will($this->returnValue($Model));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforeRender', array('success' => false));
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_get', array(), $Action);
}
/**
* Test that calling HTTP POST on an add action
* will trigger multiple events on success
*
* @covers AddCrudAction::_post
* @return void
*/
public function testActionPostSuccess() {
$Action = $this->_actionSuccess();
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_post', array(), $Action);
}
/**
* Test that calling HTTP PUT on an add action
* will trigger multiple events on success
*
* @covers AddCrudAction::_put
* @return void
*/
public function testActionPutSuccess() {
$Action = $this->_actionSuccess();
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_put', array(), $Action);
}
/**
* Test that calling HTTP PUT on an add action
* will trigger multiple events on success
*
* @covers AddCrudAction::_put
* @return void
*/
public function testActionPutSuccessWithDifferentSaveMethod() {
$Action = $this->_actionSuccess('saveAll');
$Action->saveMethod('saveAll');
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_put', array(), $Action);
}
protected function _actionSuccess($saveMethod = 'saveAssociated') {
$Request = $this->getMock('CakeRequest');
$Request->data = array('Post' => array('name' => 'Hello World'));
$Model = $this->getMock('Model', array($saveMethod));
$Model
->expects($this->once())
->method($saveMethod)
->with($Request->data)
->will($this->returnCallback(function() use ($Model) {
$Model->id = 1;
return true;
}));
$Action = $this
->getMockBuilder('AddCrudAction')
->disableOriginalConstructor()
->setMethods(array('_request', '_model', '_trigger', 'setFlash', '_redirect'))
->getMock();
$AfterSaveSubject = new CrudSubject();
$i = 0;
$Action
->expects($this->at($i++))
->method('_request')
->will($this->returnValue($Request));
$Action
->expects($this->at($i++))
->method('_model')
->will($this->returnValue($Model));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforeSave');
$Action
->expects($this->at($i++))
->method('setFlash')
->with('success');
$Action
->expects($this->at($i++))
->method('_trigger')
->with('afterSave', array('success' => true, 'created' => true, 'id' => 1))
->will($this->returnValue($AfterSaveSubject));
$Action
->expects($this->at($i++))
->method('_redirect')
->with($AfterSaveSubject, array('action' => 'index'));
return $Action;
}
/**
* Test that calling HTTP POST on an add action
* will trigger multiple events on error
*
* @covers AddCrudAction::_post
* @return void
*/
public function testActionPostError() {
$Request = $this->getMock('CakeRequest');
$Request->data = array('Post' => array('name' => 'Hello World'));
$Model = $this->getMock('Model', array('saveAssociated'));
$Model->data = array('model' => true);
$Action = $this
->getMockBuilder('AddCrudAction')
->disableOriginalConstructor()
->setMethods(array('_request', '_model', '_trigger', 'setFlash'))
->getMock();
$AfterSaveSubject = new CrudSubject();
$i = 0;
$Action
->expects($this->at($i++))
->method('_request')
->will($this->returnValue($Request));
$Action
->expects($this->at($i++))
->method('_model')
->will($this->returnValue($Model));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforeSave');
$Model
->expects($this->once())
->method('saveAssociated')
->with($Request->data)
->will($this->returnValue(false));
$Action
->expects($this->at($i++))
->method('setFlash')
->with('error');
$Action
->expects($this->at($i++))
->method('_trigger')
->with('afterSave', array('success' => false, 'created' => false))
->will($this->returnValue($AfterSaveSubject));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforeRender', $AfterSaveSubject);
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_post', array(), $Action);
$result = $Request->data;
$expected = $Request->data;
$expected['model'] = true;
$this->assertEquals($expected, $result, 'The Request::$data and Model::$data was not merged');
}
/**
* Test redirection logic for "add"
*
* @return void
*/
public function testRedirectListenerWithAdd() {
$Crud = $this
->getMockBuilder('CrudComponent')
->disableOriginalConstructor()
->setMethods(null)
->getMock();
$Controller = $this
->getMockBuilder('Controller')
->disableOriginalConstructor()
->setMethods(array('redirect'))
->getMock();
$Request = new CakeRequest;
$Request->params['action'] = 'add';
$Request->data = array('_add' => 'something');
$Controller->__construct($Request, new CakeResponse);
$Crud->__construct(new ComponentCollection);
$Crud->initialize($Controller);
$Crud->mapAction('add', 'add');
$Crud->addListener('redirect');
$Crud->listener('redirect');
$Action = $Crud->action('add');
$CrudSubject = $Crud->getSubject();
$CrudSubject->success = true;
$CrudSubject->created = true;
$CrudSubject->id = 69;
$Controller
->expects($this->once())
->method('redirect')
->with(array('action' => 'add'));
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_redirect', array($CrudSubject, array('action' => 'index')), $Action);
}
/**
* Test redirection logic for "edit"
*
* @return void
*/
public function testRedirectListenerWithEdit() {
$Crud = $this
->getMockBuilder('CrudComponent')
->disableOriginalConstructor()
->setMethods(null)
->getMock();
$Controller = $this
->getMockBuilder('Controller')
->disableOriginalConstructor()
->setMethods(array('redirect'))
->getMock();
$Request = new CakeRequest;
$Request->params['action'] = 'add';
$Request->data = array('_edit' => 'something');
$Controller->__construct($Request, new CakeResponse);
$Crud->__construct(new ComponentCollection);
$Crud->initialize($Controller);
$Crud->mapAction('add', 'add');
$Crud->addListener('redirect');
$Crud->listener('redirect');
$Action = $Crud->action('add');
$CrudSubject = $Crud->getSubject();
$CrudSubject->success = true;
$CrudSubject->created = true;
$CrudSubject->id = 69;
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_redirect', array($CrudSubject, array('action' => 'index')), $Action);
$expected = array('action' => 'edit', 69);
$this->assertEquals($expected, $CrudSubject->url);
}
}

View File

@ -1,431 +0,0 @@
<?php
App::uses('Model', 'Model');
App::uses('Controller', 'Controller');
App::uses('CakeRequest', 'Network');
App::uses('CrudSubject', 'Crud.Controller/Crud');
App::uses('DeleteCrudAction', 'Crud.Controller/Crud/Action');
App::uses('CrudTestCase', 'Crud.Test/Support');
/**
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class DeleteCrudActionTest extends CrudTestCase {
/**
* testDelete
*
* test the best-case flow
*
* @covers DeleteCrudAction::_delete
* @return void
*/
public function testDeleteOnDelete() {
$Action = $this->_actionSuccess();
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_delete', array(1), $Action);
}
/**
* testDelete
*
* test the best-case flow
*
* @covers DeleteCrudAction::_post
* @return void
*/
public function testDeleteOnPost() {
$Action = $this->_actionSuccess();
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_post', array(1), $Action);
}
protected function _actionSuccess() {
$Request = $this->getMock('CakeRequest');
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->setMethods(array('escapeField', 'find', 'delete'))
->getMock();
$query = array('conditions' => array('Model.id' => 1));
$CrudSubject = new CrudSubject();
$i = 0;
$Action = $this
->getMockBuilder('DeleteCrudAction')
->disableOriginalConstructor()
->setMethods(array(
'_request', '_model', '_validateId', '_getFindMethod',
'_trigger', 'setFlash', '_redirect'
))
->getMock();
$Action
->expects($this->at($i++))
->method('_validateId')
->with(1)
->will($this->returnValue(true));
$Action
->expects($this->at($i++))
->method('_request')
->with()
->will($this->returnValue($Request));
$Action
->expects($this->at($i++))
->method('_model')
->with()
->will($this->returnValue($Model));
$Model
->expects($this->once())
->method('escapeField')
->with()
->will($this->returnValue('Model.id'));
$Action
->expects($this->at($i++))
->method('_getFindMethod')
->with('count')
->will($this->returnValue('count'));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforeFind', array('id' => 1, 'query' => $query, 'findMethod' => 'count'))
->will($this->returnValue(new CrudSubject(array('query' => $query, 'findMethod' => 'count'))));
$Model
->expects($this->once())
->method('find')
->with('count', $query)
->will($this->returnValue(1));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforeDelete', array('id' => 1))
->will($this->returnValue(new CrudSubject(array('stopped' => false))));
$Model
->expects($this->once())
->method('delete')
->with()
->will($this->returnValue(true));
$Action
->expects($this->at($i++))
->method('setFlash')
->with('success');
$Action
->expects($this->at($i++))
->method('_trigger')
->with('afterDelete', array('id' => 1, 'success' => true))
->will($this->returnValue($CrudSubject));
$Action
->expects($this->at($i++))
->method('_redirect')
->with($CrudSubject, array('action' => 'index'));
return $Action;
}
/**
* test_deleteNotFound
*
* Test the behavior when a record is not found in the database
*
* @covers DeleteCrudAction::_delete
* @expectedException NotFoundException
* @expectedExceptionMessage Not Found
* @expectedExceptionCode 404
* @return void
*/
public function test_deleteNotFound() {
$Request = $this->getMock('CakeRequest');
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->setMethods(array('escapeField', 'find', 'delete'))
->getMock();
$Controller = $this
->getMockBuilder('Controller')
->disableOriginalConstructor()
->setMethods(array('referer'))
->getMock();
$query = array('conditions' => array('Model.id' => 1));
$CrudSubject = new CrudSubject();
$i = 0;
$Action = $this
->getMockBuilder('DeleteCrudAction')
->disableOriginalConstructor()
->setMethods(array(
'_request', '_model', '_validateId', '_getFindMethod',
'_trigger', 'setFlash', '_redirect', 'message'
))
->getMock();
$Action
->expects($this->at($i++))
->method('_validateId')
->with(1)
->will($this->returnValue(true));
$Action
->expects($this->at($i++))
->method('_request')
->with()
->will($this->returnValue($Request));
$Action
->expects($this->at($i++))
->method('_model')
->with()
->will($this->returnValue($Model));
$Model
->expects($this->once())
->method('escapeField')
->with()
->will($this->returnValue('Model.id'));
$Action
->expects($this->at($i++))
->method('_getFindMethod')
->with('count')
->will($this->returnValue('count'));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforeFind', array('id' => 1, 'query' => $query, 'findMethod' => 'count'))
->will($this->returnValue(new CrudSubject(array('query' => $query, 'findMethod' => 'count'))));
$Model
->expects($this->once())
->method('find')
->with('count', $query)
->will($this->returnValue(0));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('recordNotFound', array('id' => 1));
$Action
->expects($this->at($i++))
->method('message')
->with('recordNotFound', array('id' => 1))
->will($this->returnValue(array('class' => 'NotFoundException', 'text' => 'Not Found', 'code' => 404)));
$Model
->expects($this->never())
->method('delete');
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_delete', array(1), $Action);
}
/**
* test_deleteDeleteFailed
*
* test the behavior of delete() failing
*
* @covers DeleteCrudAction::_delete
* @return void
*/
public function test_deleteDeleteFailed() {
$Request = $this->getMock('CakeRequest');
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->setMethods(array('escapeField', 'find', 'delete'))
->getMock();
$query = array('conditions' => array('Model.id' => 1));
$CrudSubject = new CrudSubject();
$i = 0;
$Action = $this
->getMockBuilder('DeleteCrudAction')
->disableOriginalConstructor()
->setMethods(array(
'_request', '_model', '_validateId', '_getFindMethod',
'_trigger', 'setFlash', '_redirect'
))
->getMock();
$Action
->expects($this->at($i++))
->method('_validateId')
->with(1)
->will($this->returnValue(true));
$Action
->expects($this->at($i++))
->method('_request')
->with()
->will($this->returnValue($Request));
$Action
->expects($this->at($i++))
->method('_model')
->with()
->will($this->returnValue($Model));
$Model
->expects($this->once())
->method('escapeField')
->with()
->will($this->returnValue('Model.id'));
$Action
->expects($this->at($i++))
->method('_getFindMethod')
->with('count')
->will($this->returnValue('count'));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforeFind', array('id' => 1, 'query' => $query, 'findMethod' => 'count'))
->will($this->returnValue(new CrudSubject(array('query' => $query, 'findMethod' => 'count'))));
$Model
->expects($this->once())
->method('find')
->with('count', $query)
->will($this->returnValue(1));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforeDelete', array('id' => 1))
->will($this->returnValue(new CrudSubject(array('stopped' => false))));
$Model
->expects($this->once())
->method('delete')
->with()
->will($this->returnValue(false));
$Action
->expects($this->at($i++))
->method('setFlash')
->with('error');
$Action
->expects($this->at($i++))
->method('_trigger')
->with('afterDelete', array('id' => 1, 'success' => false))
->will($this->returnValue($CrudSubject));
$Action
->expects($this->at($i++))
->method('_redirect')
->with($CrudSubject, array('action' => 'index'));
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_delete', array(1), $Action);
}
/**
* test_deleteDeleteStoppedByEvent
*
* test the behavior when the beforeDelete callback
* stops the event
*
* @covers DeleteCrudAction::_delete
* @return void
*/
public function test_deleteDeleteStoppedByEvent() {
$Request = $this->getMock('CakeRequest');
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->setMethods(array('escapeField', 'find', 'delete'))
->getMock();
$query = array('conditions' => array('Model.id' => 1));
$CrudSubject = new CrudSubject();
$i = 0;
$Action = $this
->getMockBuilder('DeleteCrudAction')
->disableOriginalConstructor()
->setMethods(array(
'_request', '_model', '_validateId', '_getFindMethod',
'_trigger', 'setFlash', '_redirect'
))
->getMock();
$Action
->expects($this->at($i++))
->method('_validateId')
->with(1)
->will($this->returnValue(true));
$Action
->expects($this->at($i++))
->method('_request')
->with()
->will($this->returnValue($Request));
$Action
->expects($this->at($i++))
->method('_model')
->with()
->will($this->returnValue($Model));
$Model
->expects($this->once())
->method('escapeField')
->with()
->will($this->returnValue('Model.id'));
$Action
->expects($this->at($i++))
->method('_getFindMethod')
->with('count')
->will($this->returnValue('count'));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforeFind', array('id' => 1, 'query' => $query, 'findMethod' => 'count'))
->will($this->returnValue(new CrudSubject(array('query' => $query, 'findMethod' => 'count'))));
$Model
->expects($this->once())
->method('find')
->with('count', $query)
->will($this->returnValue(1));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforeDelete', array('id' => 1))
->will($this->returnValue(new CrudSubject(array('stopped' => true))));
$Model
->expects($this->never())
->method('delete');
$Action
->expects($this->at($i++))
->method('setFlash')
->with('error');
$CrudSubject->stopped = true;
$Action
->expects($this->at($i++))
->method('_redirect')
->with($CrudSubject, array('action' => 'index'));
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_delete', array(1), $Action);
}
/**
* test_deleteInvalidId
*
* Test the behavior when the ID is invalid
*
* @covers DeleteCrudAction::_delete
* @return void
*/
public function test_deleteInvalidId() {
$Action = $this
->getMockBuilder('DeleteCrudAction')
->disableOriginalConstructor()
->setMethods(array('_model', '_validateId'))
->getMock();
$Action
->expects($this->once())
->method('_validateId')
->with(1)
->will($this->returnValue(false));
$Action
->expects($this->never())
->method('_model');
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_delete', array(1), $Action);
}
}

View File

@ -1,341 +0,0 @@
<?php
App::uses('Model', 'Model');
App::uses('Controller', 'Controller');
App::uses('CakeRequest', 'Network');
App::uses('PaginatorComponent', 'Controller/Component');
App::uses('ComponentCollection', 'Controller');
App::uses('CrudSubject', 'Crud.Controller/Crud');
App::uses('IndexCrudAction', 'Crud.Controller/Crud/Action');
App::uses('CrudTestCase', 'Crud.Test/Support');
class TestController extends Controller {
public $paginate = array();
}
/**
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class IndexCrudActionTest extends CrudTestCase {
/**
* Tests that calling index action will paginate the main model
*
* @covers IndexCrudAction::_get
* @return void
*/
public function test_get() {
$Controller = $this
->getMockBuilder('Controller')
->disableOriginalConstructor()
->setMethods(array('paginate', 'set'))
->getMock();
$Controller->Paginator = $this
->getMockBuilder('PaginatorComponent')
->disableOriginalConstructor()
->getMock();
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$i = 0;
$Action = $this
->getMockBuilder('IndexCrudAction')
->disableOriginalConstructor()
->setMethods(array('paginationConfig', '_controller', '_model', '_trigger', 'viewVar'))
->getMock();
$Action
->expects($this->at($i++))
->method('paginationConfig')
->with();
$Action
->expects($this->at($i++))
->method('_controller')
->with()
->will($this->returnValue($Controller));
$Action
->expects($this->at($i++))
->method('viewVar')
->with()
->will($this->returnValue('items'));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforePaginate', array('paginator' => $Controller->Paginator, 'success' => true, 'viewVar' => 'items'))
->will($this->returnValue(new CrudSubject(array('success' => true, 'viewVar' => 'items'))));
$Action
->expects($this->at($i++))
->method('_model')
->with()
->will($this->returnValue($Model));
$Controller
->expects($this->once())
->method('paginate')
->with($Model)
->will($this->returnValue(array('foo', 'bar')));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('afterPaginate', array('success' => true, 'viewVar' => 'items', 'items' => array('foo', 'bar')))
->will($this->returnValue(new CrudSubject(array('success' => true, 'viewVar' => 'items', 'items' => array('foo', 'bar')))));
$Controller
->expects($this->once())
->method('set')
->with(array('success' => true, 'items' => array('foo', 'bar')));
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_get', array(), $Action);
}
/**
* Tests that iterators are casted to arrays
*
* @covers IndexCrudAction::_get
* @return void
*/
public function testPaginatorReturningIterator() {
$Controller = $this
->getMockBuilder('Controller')
->disableOriginalConstructor()
->setMethods(array('paginate', 'set'))
->getMock();
$Controller->Paginator = $this
->getMockBuilder('PaginatorComponent')
->disableOriginalConstructor()
->getMock();
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$i = 0;
$Action = $this
->getMockBuilder('IndexCrudAction')
->disableOriginalConstructor()
->setMethods(array('paginationConfig', '_controller', '_model', '_trigger', 'viewVar'))
->getMock();
$Action
->expects($this->at($i++))
->method('paginationConfig')
->with();
$Action
->expects($this->at($i++))
->method('_controller')
->with()
->will($this->returnValue($Controller));
$Action
->expects($this->at($i++))
->method('viewVar')
->with()
->will($this->returnValue('items'));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforePaginate', array('paginator' => $Controller->Paginator, 'success' => true, 'viewVar' => 'items'))
->will($this->returnValue(new CrudSubject(array('success' => true, 'viewVar' => 'items'))));
$Action
->expects($this->at($i++))
->method('_model')
->with()
->will($this->returnValue($Model));
$Controller
->expects($this->once())
->method('paginate')
->with($Model)
->will($this->returnValue(array('foo', 'bar')));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('afterPaginate', array('success' => true, 'viewVar' => 'items', 'items' => array('foo', 'bar')))
->will($this->returnValue(new CrudSubject(array('success' => true, 'viewVar' => 'items', 'items' => new ArrayIterator(array('foo', 'bar'))))));
$Controller
->expects($this->once())
->method('set')
->with(array('success' => true, 'items' => array('foo', 'bar')));
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_get', array(), $Action);
}
/**
* Tests that $controller->paginate is copied to Paginator->settings
*
* @covers IndexCrudAction::paginationConfig
* @return void
*/
public function testPaginateSettingsAreMerged() {
$Controller = $this
->getMockBuilder('TestController')
->disableOriginalConstructor()
->setMethods(array('foo'))
->getMock();
$Controller->paginate = array(
'limit' => 50,
'paramType' => 'querystring'
);
$Paginator = $this
->getMockBuilder('PaginatorComponent')
->disableOriginalConstructor()
->getMock();
$Controller->Components = $this
->getMockBuilder('ComponentCollection')
->disableOriginalConstructor()
->setMethods(array('load'))
->getMock();
$Controller->Components
->expects($this->at(0))
->method('load')
->with('Paginator')
->will($this->returnValue($Paginator));
$i = 0;
$Action = $this
->getMockBuilder('IndexCrudAction')
->disableOriginalConstructor()
->setMethods(array('_controller', '_getFindMethod'))
->getMock();
$Action
->expects($this->at($i++))
->method('_controller')
->with()
->will($this->returnValue($Controller));
$Controller->Components
->expects($this->once())
->method('load')
->with('Paginator', array('limit' => 50, 'paramType' => 'querystring'))
->will($this->returnCallback(function() use ($Paginator) {
$Paginator->settings = array('limit' => 50, 'paramType' => 'querystring');
return $Paginator;
}));
$Action
->expects($this->at($i++))
->method('_getFindMethod')
->with('all')
->will($this->returnValue('all'));
$result = $Action->paginationConfig();
$expected = array(
'findType' => 'all',
'limit' => 50,
'paramType' => 'querystring'
);
$this->assertEquals($expected, $result);
}
/**
* Test that no findMethod is executed when a findType
* already is defined for a Model key
*
* @covers IndexCrudAction::paginationConfig
* @return void
*/
public function testPaginationConfigExistingFindType() {
$Controller = $this
->getMockBuilder('Controller')
->disableOriginalConstructor()
->setMethods(array('foo'))
->getMock();
$Paginator = $this
->getMockBuilder('PaginatorComponent')
->disableOriginalConstructor()
->getMock();
$Paginator->settings['MyModel'] = array(
'limit' => 5,
'findType' => 'another'
);
$Controller->Paginator = $Paginator;
$Controller->modelClass = 'MyModel';
$i = 0;
$Action = $this
->getMockBuilder('IndexCrudAction')
->disableOriginalConstructor()
->setMethods(array('_controller', '_getFindMethod'))
->getMock();
$Action
->expects($this->at($i++))
->method('_controller')
->with()
->will($this->returnValue($Controller));
$Action
->expects($this->never())
->method('_getFindMethod');
$result = $Action->paginationConfig();
$expected = array(
'MyModel' => array(
'limit' => 5,
'findType' => 'another'
),
'page' => 1,
'limit' => 20,
'maxLimit' => 100,
'paramType' => 'named'
);
$this->assertEquals($expected, $result);
}
/**
* Test that `all` findMethod is executed when a findType
* already is defined for a Model key
*
* @covers IndexCrudAction::paginationConfig
* @return void
*/
public function testPaginationConfigNonexistingFindType() {
$Controller = $this
->getMockBuilder('Controller')
->disableOriginalConstructor()
->setMethods(array('foo'))
->getMock();
$Paginator = $this
->getMockBuilder('PaginatorComponent')
->disableOriginalConstructor()
->getMock();
$Paginator->settings['MyModel'] = array(
'limit' => 5,
'findType' => null
);
$Controller->Paginator = $Paginator;
$Controller->modelClass = 'MyModel';
$i = 0;
$Action = $this
->getMockBuilder('IndexCrudAction')
->disableOriginalConstructor()
->setMethods(array('_controller', '_getFindMethod'))
->getMock();
$Action
->expects($this->at($i++))
->method('_controller')
->with()
->will($this->returnValue($Controller));
$Action
->expects($this->at($i++))
->method('_getFindMethod')
->with('all')
->will($this->returnValue('all'));
$result = $Action->paginationConfig();
$expected = array(
'MyModel' => array(
'limit' => 5,
'findType' => 'all'
),
'page' => 1,
'limit' => 20,
'maxLimit' => 100,
'paramType' => 'named'
);
$this->assertEquals($expected, $result);
}
}

View File

@ -1,365 +0,0 @@
<?php
App::uses('Model', 'Model');
App::uses('Controller', 'Controller');
App::uses('CakeRequest', 'Network');
App::uses('CrudSubject', 'Crud.Controller/Crud');
App::uses('ViewCrudAction', 'Crud.Controller/Crud/Action');
App::uses('CrudTestCase', 'Crud.Test/Support');
/**
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class ViewCrudActionTest extends CrudTestCase {
/**
* test_getGet
*
* Test that calling HTTP GET on an view action
* will trigger the appropriate events
*
* This test assumes the best possible case
*
* The id provided, it's correct and it's in the db
*
* @return void
*/
public function test_getGet() {
$query = array('conditions' => array('Model.id' => 1));
$data = array('Model' => array('id' => 1));
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->setMethods(array('escapeField', 'find'))
->getMock();
$Controller = $this
->getMockBuilder('Controller')
->disableOriginalConstructor()
->setMethods(array('set'))
->getMock();
$i = 0;
$Action = $this
->getMockBuilder('ViewCrudAction')
->disableOriginalConstructor()
->setMethods(array(
'_validateId', '_controller', '_model',
'_trigger', 'viewVar', '_getFindMethod'
))
->getMock();
$Action
->expects($this->at($i++))
->method('_validateId')
->with(1)
->will($this->returnValue(true));
$Action
->expects($this->at($i++))
->method('_model')
->with()
->will($this->returnValue($Model));
$Model
->expects($this->once())
->method('escapeField')
->with()
->will($this->returnValue('Model.id'));
$Action
->expects($this->at($i++))
->method('_getFindMethod')
->with('first')
->will($this->returnValue('first'));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforeFind', array(
'findMethod' => 'first',
'query' => $query,
'id' => 1
))
->will($this->returnValue(new CrudSubject(array('query' => $query, 'findMethod' => 'first'))));
$Model
->expects($this->once())
->method('find')
->with('first', $query)
->will($this->returnValue($data));
$Action
->expects($this->at($i++))
->method('viewVar')
->with()
->will($this->returnValue('example'));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('afterFind', array(
'id' => 1,
'item' => $data,
'viewVar' => 'example',
'success' => true
))
->will($this->returnValue(new CrudSubject(array(
'success' => true,
'viewVar' => 'example',
'id' => 1,
'item' => $data
))));
$Controller
->expects($this->once())
->method('set')
->with(array('example' => $data, 'success' => true));
$Action
->expects($this->at($i++))
->method('_controller')
->with()
->will($this->returnValue($Controller));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforeRender');
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_get', array(1), $Action);
}
/**
* test_getGetCustomViewVar
*
* Test that calling HTTP GET on an view action
* will trigger the appropriate events
*
* Testing that setting a different viewVar actually works
*
* @return void
*/
public function test_getGetCustomViewVar() {
$query = array('conditions' => array('Model.id' => 1));
$data = array('Model' => array('id' => 1));
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->setMethods(array('escapeField', 'find'))
->getMock();
$Controller = $this
->getMockBuilder('Controller')
->disableOriginalConstructor()
->setMethods(array('set'))
->getMock();
$i = 0;
$Action = $this
->getMockBuilder('ViewCrudAction')
->disableOriginalConstructor()
->setMethods(array(
'_validateId', '_controller', '_model',
'_trigger', 'viewVar', '_getFindMethod'
))
->getMock();
$Action
->expects($this->at($i++))
->method('_validateId')
->with(1)
->will($this->returnValue(true));
$Action
->expects($this->at($i++))
->method('_model')
->with()
->will($this->returnValue($Model));
$Model
->expects($this->once())
->method('escapeField')
->with()
->will($this->returnValue('Model.id'));
$Action
->expects($this->at($i++))
->method('_getFindMethod')
->with('first')
->will($this->returnValue('first'));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforeFind', array(
'findMethod' => 'first',
'query' => $query,
'id' => 1
))
->will($this->returnValue(new CrudSubject(array('query' => $query, 'findMethod' => 'first'))));
$Model
->expects($this->once())
->method('find')
->with('first', $query)
->will($this->returnValue($data));
$Action
->expects($this->at($i++))
->method('viewVar')
->with()
->will($this->returnValue('item'));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('afterFind', array(
'id' => 1,
'item' => $data,
'viewVar' => 'item',
'success' => true
))
->will($this->returnValue(new CrudSubject(array(
'item' => $data,
'success' => true,
'viewVar' => 'item'
))));
$Action
->expects($this->at($i++))
->method('_controller')
->with()
->will($this->returnValue($Controller));
$Controller
->expects($this->once())
->method('set')
->with(array('item' => $data, 'success' => true));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforeRender');
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_get', array(1), $Action);
}
/**
* test_getGetNotFound
*
* Test that calling HTTP GET on an view action
* will trigger the appropriate events
*
* The ID provided is valid, but does not exist in the database
*
* @expectedException NotFoundException
* @exepctedExceptionMessage Not Found
* @exepctedExceptionCode 404
* @return void
*/
public function test_getGetNotFound() {
$query = array('conditions' => array('Model.id' => 1));
$data = array('Model' => array('id' => 1));
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->setMethods(array('escapeField', 'find'))
->getMock();
$Controller = $this
->getMockBuilder('Controller')
->disableOriginalConstructor()
->setMethods(array('set'))
->getMock();
$i = 0;
$Action = $this
->getMockBuilder('ViewCrudAction')
->disableOriginalConstructor()
->setMethods(array(
'_validateId', '_controller', '_model',
'_trigger', 'viewVar', '_getFindMethod',
'message'
))
->getMock();
$Action
->expects($this->at($i++))
->method('_validateId')
->with(1)
->will($this->returnValue(true));
$Action
->expects($this->at($i++))
->method('_model')
->with()
->will($this->returnValue($Model));
$Model
->expects($this->once())
->method('escapeField')
->with()
->will($this->returnValue('Model.id'));
$Action
->expects($this->at($i++))
->method('_getFindMethod')
->with('first')
->will($this->returnValue('first'));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('beforeFind', array(
'findMethod' => 'first',
'query' => $query,
'id' => 1
))
->will($this->returnValue(new CrudSubject(array('query' => $query, 'findMethod' => 'first'))));
$Model
->expects($this->once())
->method('find')
->with('first', $query)
->will($this->returnValue(false));
$Action
->expects($this->at($i++))
->method('_trigger')
->with('recordNotFound', array('id' => 1));
$Action
->expects($this->at($i++))
->method('message')
->with('recordNotFound', array('id' => 1))
->will($this->returnValue(array('class' => 'NotFoundException', 'text' => 'NotFound', 'code' => 404)));
$Action
->expects($this->never())
->method('_controller');
$Action
->expects($this->never())
->method('viewVar');
$Controller
->expects($this->never())
->method('set');
$this->setReflectionClassInstance($Action);
$this->callProtectedMethod('_get', array(1), $Action);
}
/**
* test_getGetInvalidId
*
* Test that calling HTTP GET on an view action
* will trigger the appropriate events
*
* This test assumes that the id for the view
* action does not exist in the database
*
* @return void
*/
public function test_getGetInvalidId() {
$Action = $this
->getMockBuilder('ViewCrudAction')
->disableOriginalConstructor()
->setMethods(array('_validateId', '_model', 'beforeRender', '_trigger'))
->getMock();
$Action
->expects($this->once())
->method('_validateId')
->with(1)
->will($this->returnValue(false));
$Action
->expects($this->never())
->method('_model');
$Action
->expects($this->never())
->method('_trigger');
$this->setReflectionClassInstance($Action);
$result = $this->callProtectedMethod('_get', array(1), $Action);
$this->assertFalse($result);
}
}

View File

@ -1,989 +0,0 @@
<?php
App::uses('CakeEvent', 'Event');
App::uses('ComponentCollection', 'Controller');
App::uses('Controller', 'Controller');
App::uses('SessionComponent', 'Controller/Component');
App::uses('CrudAction', 'Crud.Controller/Crud');
App::uses('CrudSubject', 'Crud.Controller/Crud');
App::uses('CrudComponent', 'Crud.Controller/Component');
App::uses('CrudTestCase', 'Crud.Test/Support');
class TestHandleCrudAction extends CrudAction {
protected $_settings = array(
'enabled' => true,
);
protected function _handle() {
return false;
}
}
class TestExceptionHandlerCrudAction extends CrudAction {
protected $_settings = array(
'enabled' => true,
);
}
/**
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class CrudActionTest extends CrudTestCase {
public function setUp() {
parent::setUp();
$this->Request = $this->getMock('CakeRequest');
$this->Collection = $this->getMock('ComponentCollection', null);
$this->Controller = $this->getMock('Controller');
$this->Controller->Components = $this->Collection;
$this->Crud = $this->getMock('CrudComponent', null, array($this->Collection));
$this->Model = $this->getMock('Model');
$this->Model->name = '';
$this->action = 'add';
$this->Subject = new CrudSubject(array(
'request' => $this->Request,
'crud' => $this->Crud,
'controller' => $this->Controller,
'action' => $this->action,
'model' => $this->Model,
'modelClass' => '',
'args' => array()
));
$this->actionClassName = $this->getMockClass('CrudAction', array('_handle'));
$this->ActionClass = new $this->actionClassName($this->Subject);
$this->_configureAction($this->ActionClass);
}
public function tearDown() {
parent::tearDown();
unset(
$this->Crud,
$this->Request,
$this->Collection,
$this->Controller,
$this->action,
$this->Subject,
$this->ActionClass
);
}
protected function _configureAction($action) {
$action->config(array(
'enabled' => true,
'findMethod' => 'first',
'view' => null,
'relatedModels' => true,
'validateId' => null,
'saveOptions' => array(
'validate' => 'first',
'atomic' => true
),
'serialize' => array(
'success',
'data'
)
));
}
/**
* Test that it's possible to override all
* configuration settings through the __constructor()
*
* @return void
*/
public function testOverrideAllDefaults() {
$expected = array(
'enabled' => false,
'findMethod' => 'any',
'view' => 'my_view',
'relatedModels' => array('Tag'),
'validateId' => 'id',
'saveOptions' => array(
'validate' => 'never',
'atomic' => false
),
'serialize' => array(
'yay',
'ney'
),
'action' => 'add'
);
$ActionClass = new $this->actionClassName($this->Subject, $expected);
// This is injected by the CrudAction, not technically a setting
$expected['action'] = 'add';
$actual = $ActionClass->config();
$this->assertEquals($expected, $actual, 'It was not possible to override all default settings.');
}
/**
* Test that we get the expected events
*
* @covers CrudAction::implementedEvents
* @return void
*/
public function testImplementedEvents() {
$expected = array();
$actual = $this->ActionClass->implementedEvents();
$this->assertEquals($expected, $actual, 'The CrudAction implements events');
}
/**
* Test that an enabled action will call _handle
*
* @covers CrudAction::handle
* @return void
*/
public function testEnabledActionWorks() {
$Request = $this->getMock('CakeRequest', array('method'));
$Request->action = 'add';
$Request
->expects($this->once())
->method('method')
->will($this->returnValue('GET'));
$Action = $this
->getMockBuilder('CrudAction')
->disableOriginalConstructor()
->setMethods(array('_request', '_get'))
->getMock();
$Action
->expects($this->any())
->method('_request')
->with()
->will($this->returnValue($Request));
$Action
->expects($this->once())
->method('_get', '_handle was never called on a enabled action')
->will($this->returnValue(true));
$this->_configureAction($Action);
$Action->config('action', 'add');
$expected = true;
$actual = $Action->config('enabled');
$this->assertSame($expected, $actual, 'The action is not enabled by default');
$expected = true;
$actual = $Action->handle($this->Subject);
$this->assertSame($expected, $actual, 'Calling handle on a disabled action did not return null');
}
/**
* testDisable
*
* Test that calling disable() on the action object
* disables the action and makes the handle method return false
*
* @covers CrudAction::disable
* @return void
*/
public function testDisable() {
$Controller = $this
->getMockBuilder('Controller')
->setMethods(array('foo'))
->disableOriginalConstructor()
->getMock();
$Controller->methods = array('add', 'index', 'delete');
$i = 0;
$Action = $this
->getMockBuilder('CrudAction')
->setMethods(array('config', '_controller', '_handle'))
->disableOriginalConstructor()
->getMock();
$Action
->expects($this->at($i++))
->method('config', 'enabled was not changed to false by config()')
->with('enabled', false);
$Action
->expects($this->at($i++))
->method('_controller')
->with()
->will($this->returnValue($Controller));
$Action
->expects($this->at($i++))
->method('config')
->with('action')
->will($this->returnValue('add'));
$Action->disable();
$actual = array_search('add', $Controller->methods);
$this->assertFalse($actual, '"add" was not removed from the controller::$methods array');
}
/**
* testEnable
*
* Test that calling enable() on the action object
* enables the action
*
* @covers CrudAction::enable
* @return void
*/
public function testEnable() {
$Controller = $this
->getMockBuilder('Controller')
->setMethods(array('foo'))
->disableOriginalConstructor()
->getMock();
$i = 0;
$Action = $this
->getMockBuilder('CrudAction')
->setMethods(array('config', '_controller', '_handle'))
->disableOriginalConstructor()
->getMock();
$Action
->expects($this->at($i++))
->method('config', 'enabled was not changed to false by config()')
->with('enabled', true);
$Action
->expects($this->at($i++))
->method('_controller')
->with()
->will($this->returnValue($Controller));
$Action
->expects($this->at($i++))
->method('config')
->with('action')
->will($this->returnValue('add'));
$Action->enable();
$actual = array_search('add', $Controller->methods);
$this->assertTrue($actual !== false, '"add" was not added to the controller::$methods array');
}
/**
* Test that getting the findMethod will execute config()
*
* @covers CrudAction::findMethod
* @return void
*/
public function testFindMethodGet() {
$Action = $this
->getMockBuilder('CrudAction')
->setMethods(array('config', '_handle'))
->setConstructorArgs(array($this->Subject))
->getMock();
$Action
->expects($this->once())
->method('config')
->with('findMethod');
$Action->findMethod();
}
/**
* Test that setting the findMethod will execute config()
*
* @covers CrudAction::findMethod
* @return void
*/
public function testFindMethodSet() {
$Action = $this
->getMockBuilder('CrudAction')
->setMethods(array('config', '_handle'))
->setConstructorArgs(array($this->Subject))
->getMock();
$Action
->expects($this->once())
->method('config')
->with('findMethod', 'my_first');
$Action->findMethod('my_first');
}
/**
* Test that getting the saveMethod will execute config()
*
* @covers CrudAction::saveMethod
* @return void
*/
public function testSaveMethodGet() {
$Action = $this
->getMockBuilder('CrudAction')
->setMethods(array('config', '_handle'))
->setConstructorArgs(array($this->Subject))
->getMock();
$Action
->expects($this->once())
->method('config')
->with('saveMethod');
$Action->saveMethod();
}
/**
* Test that setting the saveMethod will execute config()
*
* @covers CrudAction::saveMethod
* @return void
*/
public function testSaveMethodSet() {
$Action = $this
->getMockBuilder('CrudAction')
->setMethods(array('config', '_handle'))
->setConstructorArgs(array($this->Subject))
->getMock();
$Action
->expects($this->once())
->method('config')
->with('saveMethod', 'my_first');
$Action->saveMethod('my_first');
}
/**
* Test that getting the saveOptions will execute config()
*
* @covers CrudAction::saveOptions
* @return void
*/
public function testSaveOptionsGet() {
$this->ActionClass = $this->getMock('CrudAction', array('config', '_handle'), array($this->Subject));
$this->ActionClass
->expects($this->once())
->method('config')
->with('saveOptions');
$this->ActionClass->saveOptions();
}
/**
* Test that setting the saveOptions will execute config()
*
* @covers CrudAction::saveOptions
* @return void
*/
public function testSaveOptionsSet() {
$this->ActionClass = $this->getMock('CrudAction', array('config', '_handle'), array($this->Subject));
$this->ActionClass
->expects($this->once())
->method('config')
->with('saveOptions', array('hello world'));
$this->ActionClass->saveOptions(array('hello world'));
}
/**
* Test that getting the view will execute config()
*
* Since there is no view configured, it will call config('action')
* and use the return value as the view name.
*
* @covers CrudAction::view
* @return void
*/
public function testViewGetWithoutConfiguredView() {
$this->Request->action = 'add';
$this->ActionClass = $this->getMock('CrudAction', array('config', '_handle'), array($this->Subject));
$this->ActionClass
->expects($this->at(0))
->method('config')
->with('view');
$expected = 'add';
$actual = $this->ActionClass->view();
$this->assertSame($expected, $actual);
}
/**
* Test that getting the view will execute config()
*
* Since a view has been configured, the view value will be
* returned and it won't use action
*
* @covers CrudAction::view
* @return void
*/
public function testViewGetWithConfiguredView() {
$this->ActionClass = $this->getMock('CrudAction', array('config', '_handle'), array($this->Subject));
$this->ActionClass
->expects($this->once())
->method('config')
->with('view')
->will($this->returnValue('add'));
$expected = 'add';
$actual = $this->ActionClass->view();
$this->assertSame($expected, $actual);
}
/**
* Test that setting the saveOptions will execute config()
*
* @covers CrudAction::view
* @return void
*/
public function testViewSet() {
$this->ActionClass = $this->getMock('CrudAction', array('config', '_handle'), array($this->Subject));
$this->ActionClass
->expects($this->once())
->method('config')
->with('view', 'my_view');
$this->ActionClass->view('my_view');
}
/**
* Test that setFlash triggers the correct methods
*
* @covers CrudAction::setFlash
* @return void
*/
public function testSetFlash() {
$data = array(
'element' => 'default',
'params' => array(
'class' => 'message success',
'original' => 'Hello'
),
'key' => 'flash',
'type' => 'add.success',
'name' => 'test',
'text' => 'Hello',
);
$object = (object)$data;
$this->Subject->crud = $this->getMock('CrudComponent', array('trigger'), array($this->Collection));
$this->Subject->crud
->expects($this->once())
->method('trigger')
->with('setFlash', $data)
->will($this->returnValue($object));
$this->Subject->crud->Session = $this->getMock('SessionComponent', array('setFlash'), array($this->Collection));
$this->Subject->crud->Session
->expects($this->once())
->method('setFlash')
->with($object->text, $object->element, $object->params, $object->key);
$this->ActionClass = new $this->actionClassName($this->Subject);
$this->ActionClass->config('name', 'test');
$this->ActionClass->config('messages', array('success' => array('text' => 'hello')));
$this->ActionClass->setFlash('success');
}
/**
* Test that detecting the correct validation strategy for validateId
* works as expected
*
* @covers CrudAction::detectPrimaryKeyFieldType
* @return void
*/
public function testDetectPrimaryKeyFieldType() {
$Model = $this->getMock('Model', array('schema'));
$Model
->expects($this->at(0))
->method('schema')
->with('id')
->will($this->returnValue(false));
$Model
->expects($this->at(1))
->method('schema')
->with('id')
->will($this->returnValue(array('length' => 36, 'type' => 'string')));
$Model
->expects($this->at(2))
->method('schema')
->with('id')
->will($this->returnValue(array('length' => 10, 'type' => 'integer')));
$Model
->expects($this->at(3))
->method('schema')
->with('id')
->will($this->returnValue(array('length' => 10, 'type' => 'string')));
$this->assertFalse($this->ActionClass->detectPrimaryKeyFieldType($Model));
$this->assertSame('uuid', $this->ActionClass->detectPrimaryKeyFieldType($Model));
$this->assertSame('integer', $this->ActionClass->detectPrimaryKeyFieldType($Model));
$this->assertFalse($this->ActionClass->detectPrimaryKeyFieldType($Model));
}
/**
* Test default saveAll options works when modified
*
* @covers CrudAction::saveOptions
* @return void
*/
public function testGetSaveAllOptionsDefaults() {
$CrudAction = $this->ActionClass;
$expected = array(
'validate' => 'first',
'atomic' => true
);
$actual = $CrudAction->config('saveOptions');
$this->assertEquals($expected, $actual);
$CrudAction->config('saveOptions.atomic', true);
$expected = array(
'validate' => 'first',
'atomic' => true
);
$actual = $CrudAction->config('saveOptions');
$this->assertEquals($expected, $actual);
$CrudAction->config('saveOptions', array(
'fieldList' => array('hello')
));
$expected = array(
'validate' => 'first',
'atomic' => true,
'fieldList' => array('hello')
);
$actual = $CrudAction->config('saveOptions');
$this->assertEquals($expected, $actual);
}
/**
* Test that defining specific action configuration for saveAll takes
* precedence over default configurations
*
* @covers CrudAction::saveOptions
* @return void
*/
public function testGetSaveAllOptionsCustomAction() {
$expected = array('validate' => 'first', 'atomic' => true);
$actual = $this->ActionClass->saveOptions();
$this->assertEquals($expected, $actual);
$this->ActionClass->saveOptions(array('atomic' => false));
$expected = array('validate' => 'first', 'atomic' => false);
$actual = $this->ActionClass->saveOptions();
$this->assertEquals($expected, $actual);
}
/**
* testEmptyMessage
*
* @covers CrudAction::message
* @expectedException CakeException
* @expectedExceptionMessage Missing message type
*/
public function testEmptyMessage() {
$this->ActionClass->message(null);
}
/**
* testUndefinedMessage
*
* @covers CrudAction::message
* @expectedException CakeException
* @expectedExceptionMessage Invalid message type "not defined"
*/
public function testUndefinedMessage() {
$this->ActionClass->message('not defined');
}
/**
* testBadMessageConfig
*
* @covers CrudAction::message
* @expectedException CakeException
* @expectedExceptionMessage Invalid message config for "badConfig" no text key found
*/
public function testBadMessageConfig() {
$this->Crud->config('messages.badConfig', array('foo' => 'bar'));
$this->ActionClass->message('badConfig');
}
/**
* testInheritedSimpleMessage
*
* @return void
*/
public function testInheritedSimpleMessage() {
$this->Crud->config('messages.simple', 'Simple message');
$expected = array(
'element' => 'default',
'params' => array(
'class' => 'message simple',
'original' => 'Simple message'
),
'key' => 'flash',
'type' => 'add.simple',
'name' => '',
'text' => 'Simple message'
);
$actual = $this->ActionClass->message('simple');
$this->assertEquals($expected, $actual);
}
/**
* testOverridenSimpleMessage
*
* @covers CrudAction::message
* @return void
*/
public function testOverridenSimpleMessage() {
$this->Crud->config('messages.simple', 'Simple message');
$this->ActionClass->config('messages.simple', 'Overridden message');
$expected = array(
'element' => 'default',
'params' => array(
'class' => 'message simple',
'original' => 'Overridden message'
),
'key' => 'flash',
'type' => 'add.simple',
'name' => '',
'text' => 'Overridden message'
);
$actual = $this->ActionClass->message('simple');
$this->assertEquals($expected, $actual);
}
/**
* testSimpleMessage
*
* @covers CrudAction::message
* @return void
*/
public function testSimpleMessage() {
$this->ActionClass->config('messages.simple', 'Simple message');
$expected = array(
'element' => 'default',
'params' => array(
'class' => 'message simple',
'original' => 'Simple message'
),
'key' => 'flash',
'type' => 'add.simple',
'name' => '',
'text' => 'Simple message'
);
$actual = $this->ActionClass->message('simple');
$this->assertEquals($expected, $actual);
}
/**
* testSimpleMessageWithPlaceholders
*
* @covers CrudAction::message
* @return void
*/
public function testSimpleMessageWithPlaceholders() {
$this->Crud->config('messages.simple', 'Simple message with id "{id}"');
$expected = array(
'element' => 'default',
'params' => array(
'class' => 'message simple',
'original' => 'Simple message with id "{id}"'
),
'key' => 'flash',
'type' => 'add.simple',
'name' => '',
'text' => 'Simple message with id "123"'
);
$actual = $this->ActionClass->message('simple', array('id' => 123));
$this->assertEquals($expected, $actual);
}
/**
* testInvalidIdMessage
*
* @covers CrudAction::message
* @return void
*/
public function testInvalidIdMessage() {
$expected = array(
'code' => 400,
'class' => 'BadRequestException',
'element' => 'default',
'params' => array(
'class' => 'message invalidId',
'original' => 'Invalid id'
),
'key' => 'flash',
'type' => 'add.invalidId',
'name' => '',
'text' => 'Invalid id'
);
$actual = $this->ActionClass->message('invalidId');
$this->assertEquals($expected, $actual);
}
/**
* testMessageNotFound
*
* @covers CrudAction::message
* @return void
*/
public function testRecordNotFoundMessage() {
$expected = array(
'code' => 404,
'class' => 'NotFoundException',
'element' => 'default',
'params' => array(
'class' => 'message recordNotFound',
'original' => 'Not found'
),
'key' => 'flash',
'type' => 'add.recordNotFound',
'name' => '',
'text' => 'Not found'
);
$actual = $this->ActionClass->message('recordNotFound');
$this->assertEquals($expected, $actual);
}
/**
* testBadRequestMethodMessage
*
* @covers CrudAction::message
* @return void
*/
public function testBadRequestMethodMessage() {
$expected = array(
'code' => 405,
'class' => 'MethodNotAllowedException',
'element' => 'default',
'params' => array(
'class' => 'message badRequestMethod',
'original' => 'Method not allowed. This action permits only {methods}'
),
'key' => 'flash',
'type' => 'add.badRequestMethod',
'name' => '',
'text' => 'Method not allowed. This action permits only THESE ONES'
);
$actual = $this->ActionClass->message('badRequestMethod', array('methods' => 'THESE ONES'));
$this->assertEquals($expected, $actual);
}
/**
* testHandle
*
* Test that calling handle will invoke _handle
* when the action is enabbled
*
* @covers CrudAction::handle
* @return void
*/
public function testHandle() {
$Action = $this
->getMockBuilder('CrudAction')
->disableOriginalConstructor()
->setMethods(array('config', '_get', '_request'))
->getMock();
$Request = $this->getMock('CakeRequest', array('method'));
$Request
->expects($this->once())
->method('method')
->will($this->returnValue('GET'));
$i = 0;
$Action
->expects($this->at($i++))
->method('config')
->with('enabled')
->will($this->returnValue(true));
$Action
->expects($this->at($i++))
->method('_request')
->will($this->returnValue($Request));
$Action
->expects($this->at($i++))
->method('_get');
$Action->handle(new CrudSubject(array('args' => array())));
}
/**
* testHandleDisabled
*
* Test that calling handle will not invoke _handle
* when the action is disabled
*
* @covers CrudAction::handle
* @return void
*/
public function testHandleDisabled() {
$Action = $this
->getMockBuilder('CrudAction')
->disableOriginalConstructor()
->setMethods(array('config', '_handle'))
->getMock();
$i = 0;
$Action
->expects($this->at($i++))
->method('config')
->with('enabled')
->will($this->returnValue(false));
$Action
->expects($this->never())
->method('_handle');
$Action->handle(new CrudSubject(array('args' => array())));
}
/**
* testGenericHandle
*
* Test that calling handle will invoke _handle
* when the requestType handler is not available
*
* @covers CrudAction::handle
* @return void
*/
public function testGenericHandle() {
$Action = $this
->getMockBuilder('CrudAction')
->disableOriginalConstructor()
->setMethods(array('config', '_handle', '_request'))
->getMock();
$Request = $this->getMock('CakeRequest', array('method'));
$Request
->expects($this->once())
->method('method')
->will($this->returnValue('GET'));
$i = 0;
$Action
->expects($this->at($i++))
->method('config')
->with('enabled')
->will($this->returnValue(true));
$Action
->expects($this->at($i++))
->method('_request')
->will($this->returnValue($Request));
$Action
->expects($this->once())
->method('_handle');
$Action->handle(new CrudSubject(array('args' => array())));
}
/**
* testHandleException
*
* Test that calling handle will not invoke _handle
* when the action is disabled
*
* @covers CrudAction::handle
* @expectedException NotImplementedException
* @return void
*/
public function testHandleException() {
$Action = $this
->getMockBuilder('CrudAction')
->disableOriginalConstructor()
->setMethods(array('config', '_request'))
->getMock();
$Request = $this->getMock('CakeRequest', array('method'));
$Request
->expects($this->once())
->method('method')
->will($this->returnValue('GET'));
$i = 0;
$Action
->expects($this->at($i++))
->method('config')
->with('enabled')
->will($this->returnValue(true));
$Action
->expects($this->at($i++))
->method('_request')
->will($this->returnValue($Request));
$Action->handle(new CrudSubject(array('args' => array())));
}
/**
* testValidateIdFalse
*
* If validateId is false - don't do squat
*
* @return void
*/
public function testValidateIdFalse() {
$Action = $this
->getMockBuilder('CrudAction')
->disableOriginalConstructor()
->setMethods(array('config', 'detectPrimaryKeyFieldType'))
->getMock();
$Action
->expects($this->once())
->method('config')
->with('validateId')
->will($this->returnValue(false));
$Action
->expects($this->never())
->method('detectPrimaryKeyFieldType');
$this->setReflectionClassInstance($Action);
$return = $this->callProtectedMethod('_validateId', array('some id'), $Action);
$this->assertTrue($return, 'If validateId is false the check should be skipped');
}
/**
* Test that getting the saveMethod will execute config()
*
* @covers CrudAction::relatedModels
* @return void
*/
public function testRelatedModelsGet() {
$Action = $this
->getMockBuilder('CrudAction')
->setMethods(array('config'))
->setConstructorArgs(array($this->Subject))
->getMock();
$Action
->expects($this->once())
->method('config')
->with('relatedModels');
$Action->relatedModels();
}
/**
* Test that setting the saveMethod will execute config()
*
* @covers CrudAction::relatedModels
* @return void
*/
public function testRelatedModelsSet() {
$Action = $this
->getMockBuilder('CrudAction')
->setMethods(array('config'))
->setConstructorArgs(array($this->Subject))
->getMock();
$Action
->expects($this->once())
->method('config')
->with('relatedModels', 'Tag', false);
$Action->relatedModels('Tag');
}
}

View File

@ -1,19 +0,0 @@
<?php
/**
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class CrudListenerTest extends CakeTestCase {
/**
* Not much going on here at the moment
* Everything is tested through the other classes
* depending on this one
*/
public function testNothing() {
$this->assertTrue(true);
}
}

View File

@ -1,83 +0,0 @@
<?php
App::uses('CrudSubject', 'Crud.Controller/Crud');
/**
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class CrudSubjectTest extends CakeTestCase {
public function setup() {
parent::setup();
$this->Subject = new CrudSubject(array('action' => 'index'));
}
public function teardown() {
parent::teardown();
unset($this->Subject);
}
/**
* Test that shouldProcess works
*
* Our action is "index"
*
* @covers CrudSubject::shouldProcess
* @return void
*/
public function testShouldProcess() {
$this->assertTrue($this->Subject->shouldProcess('only', 'index'));
$this->assertFalse($this->Subject->shouldProcess('only', 'view'));
$this->assertTrue($this->Subject->shouldProcess('only', array('index')));
$this->assertFalse($this->Subject->shouldProcess('only', array('view')));
$this->assertFalse($this->Subject->shouldProcess('not', array('index')));
$this->assertTrue($this->Subject->shouldProcess('not', array('view')));
$this->assertFalse($this->Subject->shouldProcess('not', 'index'));
$this->assertTrue($this->Subject->shouldProcess('not', 'view'));
}
/**
* Test that event adding works
*
* @covers CrudSubject::addEvent
* @covers CrudSubject::getEvents
* @covers CrudSubject::hasEvent
* @return void
*/
public function testEventNames() {
$this->assertFalse($this->Subject->hasEvent('test'));
$this->assertFalse($this->Subject->hasEvent('test_two'));
$this->assertFalse($this->Subject->hasEvent('test_three'));
$this->assertFalse($this->Subject->hasEvent('invalid'));
$this->Subject->addEvent('test');
$this->Subject->addEvent('test_two');
$this->Subject->addEvent('test_three');
$this->assertTrue($this->Subject->hasEvent('test'));
$this->assertTrue($this->Subject->hasEvent('test_two'));
$this->assertTrue($this->Subject->hasEvent('test_three'));
$this->assertFalse($this->Subject->hasEvent('invalid'));
$expected = array('test', 'test_two', 'test_three');
$this->assertEquals($expected, $this->Subject->getEvents());
}
/**
* testInvalidMode
*
* @covers CrudSubject::shouldProcess
* @expectedException CakeException
* @expectedExceptionMessage Invalid mode
* @return void
*/
public function testInvalidMode() {
$this->Subject->shouldProcess('invalid');
}
}

View File

@ -1,438 +0,0 @@
<?php
App::uses('Model', 'Model');
App::uses('Controller', 'Controller');
App::uses('PaginatorComponent', 'Controller/Component');
App::uses('CakeRequest', 'Network');
App::uses('CakeEvent', 'Event');
App::uses('CrudSubject', 'Crud.Controller/Crud');
App::uses('IndexCrudAction', 'Crud.Controller/Crud/Action');
App::uses('ApiFieldFilterListener', 'Crud.Controller/Crud/Listener');
/**
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class ApiFieldFilterListenerTest extends CakeTestCase {
public function setUp() {
parent::setUp();
$this->ModelMock = $this->getMockBuilder('Model');
$this->ControllerMock = $this->getMockBuilder('Controller');
$this->RequestMock = $this->getMockBuilder('CakeRequest');
$this->CrudMock = $this->getMockBuilder('CrudComponent');
$this->PaginatorMock = $this->getMockBuilder('PaginatorComponent');
$this->ActionMock = $this->getMockBuilder('IndexCrudAction');
}
public function tearDown() {
parent::tearDown();
unset(
$this->ModelMock,
$this->Controller,
$this->RequestMock,
$this->CrudMock,
$this->PaginatorMock,
$this->ActionMock
);
}
/**
* Helper method to generate and mock all the required
* classes
*
* `$hasField` is a field => bool array with what
* fields should exist according to 'hasField' model check
*
* @param array $hasField
* @return array
*/
protected function _mockClasses($hasField = array()) {
$CrudSubject = new CrudSubject();
$Crud = $this->CrudMock
->disableOriginalConstructor()
->setMethods(array('action'))
->getMock();
$Model = $this->ModelMock
->setConstructorArgs(array(
array('table' => 'models', 'name' => 'Model', 'ds' => 'test')
))
->setMethods(array('hasField', 'getAssociated'))
->getMock();
$Model
->expects($this->any())
->method('getAssociated')
->will($this->returnValue(array('Sample' => array(), 'Demo' => array(), 'User' => array())));
$Model->alias = 'Model';
$Controller = $this->ControllerMock
->disableOriginalConstructor()
->setMethods(null)
->getMock();
$Controller->Components = new StdClass;
$Request = new CakeRequest();
$Request->addDetector('api', array('callback' => function() {
return true;
}));
$Paginator = $this->PaginatorMock
->disableOriginalConstructor()
->setMethods(null)
->getMock();
$Controller->Paginator = $Paginator;
$CrudSubject->set(array(
'crud' => $Crud,
'request' => $Request,
'controller' => $Controller,
'action' => 'add',
'model' => $Model,
'modelClass' => $Model->name,
'args' => array(),
'query' => array(
'fields' => null,
'contain' => null
)
));
$Action = $this->ActionMock
->setConstructorArgs(array($CrudSubject))
->setMethods(null)
->getMock();
$Listener = new ApiFieldFilterListener($CrudSubject);
$Event = new CakeEvent('Test', $CrudSubject);
$Crud
->expects($this->any())
->method('action')
->will($this->returnValue($Action));
$i = 0;
foreach ($hasField as $field => $has) {
$Model
->expects($this->at($i))
->method('hasField')
->with($field)
->will($this->returnValue($has));
$i++;
}
return compact('Crud', 'Model', 'Controller', 'Paginator', 'Request', 'CrudSubject', 'Listener', 'Action', 'Event');
}
/**
* Test that the listener listen to the correct
* events with the correct priority
*
* @return void
*/
public function testImplementedEvents() {
extract($this->_mockClasses());
$expected = array(
'Crud.beforePaginate' => array('callable' => 'beforePaginate', 'priority' => 50),
'Crud.beforeFind' => array('callable' => 'beforeFind', 'priority' => 50)
);
$actual = $Listener->implementedEvents();
$this->assertEquals($expected, $actual);
}
/**
* Test that a beforeFind with no fields in the query
* will not inject any fields or contain into the query
*
* @return void
*/
public function testRequestWithoutFieldsWithNoFilterOn() {
extract($this->_mockClasses());
$Listener->allowNoFilter(true);
$Listener->beforeFind($Event);
$this->assertNull($CrudSubject->query['fields']);
}
/**
* Test that a beforeFind with no fields in the query
* will throw an exception by default
*
* @expectedException CakeException
* @expectedExceptionMessage Please specify which fields you would like to select
* @return void
*/
public function testRequestWithoutFieldsWithNoFilterDefault() {
extract($this->_mockClasses());
$Listener->beforeFind($Event);
$this->assertNull($CrudSubject->query['fields']);
}
/**
* Test that a beforeFind with no fields in the query
* will throw an exception if 'allowNofilter' is set to false
*
* @expectedException CakeException
* @expectedExceptionMessage Please specify which fields you would like to select
* @return void
*/
public function testRequestWithoutFieldsWithNoFilterOff() {
extract($this->_mockClasses());
$Action->config('apiFieldFilter.allowNoFilter', false);
$Listener->beforeFind($Event);
$this->assertNull($CrudSubject->query['fields']);
}
/**
* Test that a beforeFind with 3 fields
* will inject them into the fields array
*
* @return void
*/
public function testRequestWithFields() {
$hasField = array('id' => true, 'name' => true, 'password' => true);
extract($this->_mockClasses($hasField));
$Request->query['fields'] = 'id,name,password';
$Listener->beforeFind($Event);
$expected = array('Model.id', 'Model.name', 'Model.password');
$actual = $CrudSubject->query['fields'];
$this->assertSame($expected, $actual);
}
/**
* Test that a beforeFind with 3 fields
* will inject two into the fields array
* since they exist in the model, but the 3rd
* field (password) will be removed
*
* @return void
*/
public function testGetFieldsIncludeFieldNotInModel() {
$hasField = array('id' => true, 'name' => true, 'password' => false);
extract($this->_mockClasses($hasField));
$Request->query['fields'] = 'id,name,password';
$Listener->beforeFind($Event);
$expected = array('Model.id', 'Model.name');
$actual = $CrudSubject->query['fields'];
$this->assertSame($expected, $actual);
}
/**
* Test that whitelisting only will allow
* fields in the whitelist to be included
* in the fieldlist
*
* Password exist as a column, but is not
* whitelisted, and thus should be removed
*
* @return void
*/
public function testWhitelistFields() {
$hasField = array('id' => true, 'name' => true, 'password' => true);
extract($this->_mockClasses($hasField));
$Request->query['fields'] = 'id,name,password';
$Listener->whitelistfields(array('Model.id', 'Model.name'));
$Listener->beforeFind($Event);
$expected = array('Model.id', 'Model.name');
$actual = $CrudSubject->query['fields'];
$this->assertSame($expected, $actual);
}
/**
* Test that blacklisting a field will ensure
* that it will be removed from list of fields
*
* Password exist as a column, but is
* blacklisted, and thus should be removed
*
* @return void
*/
public function testBlacklistFields() {
$hasField = array('id' => true, 'name' => true, 'password' => true);
extract($this->_mockClasses($hasField));
$Request->query['fields'] = 'id,name,password';
$Listener->blacklistFields(array('Model.password'));
$Listener->beforeFind($Event);
$expected = array('Model.id', 'Model.name');
$actual = $CrudSubject->query['fields'];
$this->assertSame($expected, $actual);
}
/**
* Test that the field Sample.my_fk gets rejected since there is no
* whitelist for the associated model "Sample"
*
* @return void
*/
public function testAssociatedModelGetsRejectedByDefault() {
$hasField = array('id' => true, 'name' => true, 'password' => true);
extract($this->_mockClasses($hasField));
$Request->query['fields'] = 'id,name,password,Sample.my_fk';
$Listener->beforeFind($Event);
$expected = array('Model.id', 'Model.name', 'Model.password');
$actual = $CrudSubject->query['fields'];
$this->assertSame($expected, $actual);
}
/**
* Test that the field Sample.my_fk gets rejected since there is no
* whitelist for the associated model "Sample"
*
* @return void
*/
public function testAssociatedModelWhitelist() {
$hasField = array('id' => true, 'name' => true, 'password' => true);
extract($this->_mockClasses($hasField));
$Request->query['fields'] = 'id,name,password,Sample.my_fk';
$Model->Sample = $this->getMock('Model', array('hasField'), array(array('Sample' => array(), 'Demo' => array(), 'User' => array())));
$Model->Sample
->expects($this->at(0))
->method('hasField')
->with('my_fk')
->will($this->returnValue(true));
$Listener->whitelistModels(array('Sample'));
$Listener->beforeFind($Event);
$expected = array('Model.id', 'Model.name', 'Model.password', 'Sample.my_fk');
$actual = $CrudSubject->query['fields'];
$this->assertSame($expected, $actual);
}
/**
* Test that blacklisting always will win
* in the filtering.
*
* If a field is both white and blacklisted
* it will end up being removed
*
* @return void
*/
public function testBlacklistingWinOverWhitelist() {
$hasField = array('id' => true, 'name' => true, 'password' => true);
extract($this->_mockClasses($hasField));
$Request->query['fields'] = 'id,name,password';
$Listener->whitelistFields(array('Model.id', 'Model.name', 'Model.password'));
$Listener->blacklistFields(array('Model.password'));
$Listener->beforeFind($Event);
$expected = array('Model.id', 'Model.name');
$actual = $CrudSubject->query['fields'];
$this->assertSame($expected, $actual);
}
/**
* Test that a beforePaginate with no fields in the query
* will not inject any fields or contain into the query
*
* @return void
*/
public function testBeforePaginateRequestWithoutFieldsWithNoFilterOn() {
extract($this->_mockClasses());
$Listener->allowNoFilter(true);
$Listener->beforePaginate($Event);
$this->assertFalse(isset($Paginator->settings['fields']));
$this->assertFalse(isset($Paginator->settings['contain']));
}
/**
* Test that a beforePaginate with no fields in the query
* will throw an exception by default
*
* @expectedException CakeException
* @expectedExceptionMessage Please specify which fields you would like to select
* @return void
*/
public function testBeforePaginateRequestWithoutFieldsWithNoFilterDefault() {
extract($this->_mockClasses());
$Listener->beforePaginate($Event);
$this->assertFalse(isset($Paginator->settings['fields']));
$this->assertFalse(isset($Paginator->settings['contain']));
}
/**
* Test that a beforePaginate with no fields in the query
* will throw an exception if 'allowNofilter' is set to false
*
* @expectedException CakeException
* @expectedExceptionMessage Please specify which fields you would like to select
* @return void
*/
public function testBeforePaginateRequestWithoutFieldsWithNoFilterOff() {
extract($this->_mockClasses());
$Action->config('apiFieldFilter.allowNoFilter', false);
$Listener->beforePaginate($Event);
$this->assertFalse(isset($Paginator->settings['fields']));
$this->assertFalse(isset($Paginator->settings['contain']));
}
/**
* Test that a beforePaginate with 3 fields
* will inject them into the fields array
*
* @return void
*/
public function testBeforePaginateRequestWithFields() {
$hasField = array('id' => true, 'name' => true, 'password' => true);
extract($this->_mockClasses($hasField));
$Request->query['fields'] = 'id,name,password';
$Listener->beforePaginate($Event);
$expected = array('Model.id', 'Model.name', 'Model.password');
$actual = $Paginator->settings['fields'];
$this->assertSame($expected, $actual);
$this->assertTrue(isset($Paginator->settings['contain']));
$this->assertEmpty($Paginator->settings['contain']);
}
/**
* Test that a beforePaginate with 3 fields
* will inject two into the fields array
* since they exist in the model, but the 3rd
* field (password) will be removed
*
* @return void
*/
public function testBeforePaginateGetFieldsIncludeFieldNotInModel() {
$hasField = array('id' => true, 'name' => true, 'password' => false);
extract($this->_mockClasses($hasField));
$Request->query['fields'] = 'id,name,password';
$Listener->beforePaginate($Event);
$expected = array('Model.id', 'Model.name');
$actual = $Paginator->settings['fields'];
$this->assertSame($expected, $actual);
$this->assertTrue(isset($Paginator->settings['contain']));
$this->assertEmpty($Paginator->settings['contain']);
}
}

View File

@ -1,167 +0,0 @@
<?php
App::uses('Controller', 'Controller');
App::uses('CakeEvent', 'Event');
App::uses('CakeRequest', 'Network');
App::uses('CrudSubject', 'Crud.Controller/Crud');
App::uses('ApiPaginationListener', 'Crud.Controller/Crud/Listener');
/**
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class ApiPaginationListenerTest extends CakeTestCase {
/**
* Test implemented events
*
* @covers ApiPaginationListener::implementedEvents
* @return void
*/
public function testImplementedEvents() {
$Instance = new ApiPaginationListener(new CrudSubject());
$result = $Instance->implementedEvents();
$expected = array('Crud.beforeRender' => array('callable' => 'beforeRender', 'priority' => 75));
$this->assertEquals($expected, $result);
}
/**
* Test that non-API requests don't get processed
*
* @covers ApiPaginationListener::beforeRender
* @return void
*/
public function testBeforeRenderNotApi() {
$Request = $this->getMock('CakeRequest', array('is'));
$Request
->expects($this->once())
->method('is')
->with('api')
->will($this->returnValue(false));
$Crud = $this->getMock('stdClass', array('action'));
$Crud
->expects($this->never())
->method('action');
$Instance = new ApiPaginationListener(new CrudSubject(array('request' => $Request, 'crud' => $Crud)));
$Instance->beforeRender(new CakeEvent('something'));
}
/**
* Test that API requests do not get processed
* if there is no pagination data
*
* @covers ApiPaginationListener::beforeRender
* @return void
*/
public function testBeforeRenderNoPaginationData() {
$Request = $this->getMock('CakeRequest', array('is'));
$Request->paging = array('MyModel' => array());
$Request
->expects($this->once())
->method('is')
->with('api')
->will($this->returnValue(true));
$Crud = $this->getMock('stdClass', array('action'));
$Crud
->expects($this->never())
->method('action');
$CrudSubject = new CrudSubject(array('request' => $Request, 'crud' => $Crud, 'modelClass' => 'AnotherModel'));
$Instance = new ApiPaginationListener($CrudSubject);
$Instance->beforeRender(new CakeEvent('something', $CrudSubject));
}
/**
* Test that API requests do not get processed
* if there if pagination data is NULL
*
* @covers ApiPaginationListener::beforeRender
* @return void
*/
public function testBeforeRenderPaginationDataIsNull() {
$Request = $this->getMock('CakeRequest', array('is'));
$Request->paging = null;
$Request
->expects($this->once())
->method('is')
->with('api')
->will($this->returnValue(true));
$Crud = $this->getMock('stdClass', array('action'));
$Crud
->expects($this->never())
->method('action');
$CrudSubject = new CrudSubject(array('request' => $Request, 'crud' => $Crud, 'modelClass' => 'AnotherModel'));
$Instance = new ApiPaginationListener($CrudSubject);
$Instance->beforeRender(new CakeEvent('something', $CrudSubject));
}
/**
* Test that API requests do get processed
* if there is pagination data
*
* @covers ApiPaginationListener::beforeRender
* @return void
*/
public function testBeforeRenderWithPaginationData() {
$Request = $this->getMock('CakeRequest', array('is'));
$Request->paging = array('MyModel' => array(
'pageCount' => 10,
'page' => 2,
'nextPage' => true,
'prevPage' => true,
'count' => 100,
'limit' => 10
));
$expected = array(
'page_count' => 10,
'current_page' => 2,
'has_next_page' => true,
'has_prev_page' => true,
'count' => 100,
'limit' => 10
);
$Request
->expects($this->once())
->method('is')
->with('api')
->will($this->returnValue(true));
$Controller = $this->getMock('stdClass', array('set'));
$Controller
->expects($this->once())
->method('set')
->with('pagination', $expected);
$Action = $this->getMock('stdClass', array('config'));
$Action
->expects($this->once())
->method('config')
->with('serialize.pagination', 'pagination');
$Crud = $this->getMock('stdClass', array('action'));
$Crud
->expects($this->once())
->method('action')
->will($this->returnValue($Action));
$CrudSubject = new CrudSubject(array(
'request' => $Request,
'crud' => $Crud,
'controller' => $Controller,
'modelClass' => 'MyModel'
));
$Instance = new ApiPaginationListener($CrudSubject);
$Instance->beforeRender(new CakeEvent('something', $CrudSubject));
}
}

View File

@ -1,196 +0,0 @@
<?php
App::uses('Controller', 'Controller');
App::uses('CakeEvent', 'Event');
App::uses('CakeRequest', 'Network');
App::uses('CrudSubject', 'Crud.Controller/Crud');
App::uses('ApiQueryLogListener', 'Crud.Controller/Crud/Listener');
/**
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class ApiQueryLogListenerTest extends CakeTestCase {
protected $_debug;
public function setUp() {
parent::setUp();
$this->_debug = Configure::read('debug');
}
public function tearDown() {
parent::tearDown();
Configure::write('debug', $this->_debug);
}
/**
* Test implemented events
*
* @covers ApiQueryLogListener::implementedEvents
* @return void
*/
public function testImplementedEvents() {
$Instance = new ApiQueryLogListener(new CrudSubject());
$result = $Instance->implementedEvents();
$expected = array('Crud.beforeRender' => array('callable' => 'beforeRender', 'priority' => 75));
$this->assertEquals($expected, $result);
}
/**
* Test that calling beforeRender with debug 0
* will not ask for request type
*
* @covers ApiQueryLogListener::beforeRender
* @return void
*/
public function testBeforeRenderDebugZero() {
Configure::write('debug', 0);
$Request = $this->getMock('CakeRequest', array('is'));
$Request
->expects($this->never())
->method('is');
$Instance = new ApiQueryLogListener(new CrudSubject(array('request' => $Request)));
$Instance->beforeRender(new CakeEvent('something'));
}
/**
* Test that calling beforeRender with debug 1
* will not ask for request type
*
* @covers ApiQueryLogListener::beforeRender
* @return void
*/
public function testBeforeRenderDebugOne() {
Configure::write('debug', 1);
$Request = $this->getMock('CakeRequest', array('is'));
$Request
->expects($this->never())
->method('is');
$Instance = new ApiQueryLogListener(new CrudSubject(array('request' => $Request)));
$Instance->beforeRender(new CakeEvent('something'));
}
/**
* Test that calling beforeRender with debug 2
* will ask for request type but won't ask for serialize configuration
* since it's not an API request
*
* @covers ApiQueryLogListener::beforeRender
* @return void
*/
public function testBeforeRenderDebugTwo() {
Configure::write('debug', 2);
$Request = $this->getMock('CakeRequest', array('is'));
$Request
->expects($this->once())
->method('is')
->will($this->returnValue(false));
$Crud = $this->getMock('stdClass', array('action'));
$Crud
->expects($this->never())
->method('action');
$Instance = new ApiQueryLogListener(new CrudSubject(array('request' => $Request, 'crud' => $Crud)));
$Instance->beforeRender(new CakeEvent('something'));
}
/**
* Test that calling beforeRender with debug 2
* will ask for request type and set the serialize configuration
* since it's an API request
*
* @covers ApiQueryLogListener::beforeRender
* @return void
*/
public function testBeforeRenderDebugTwoAsApi() {
Configure::write('debug', 2);
$Request = $this->getMock('CakeRequest', array('is'));
$Request
->expects($this->once())
->method('is')
->will($this->returnValue(true));
$Controller = $this->getMock('stdClass', array('set'));
$Controller
->expects($this->once())
->method('set')
->with('queryLog');
$Action = $this->getMock('stdClass', array('config'));
$Action
->expects($this->once())
->method('config')
->with('serialize.queryLog', 'queryLog');
$Crud = $this->getMock('stdClass', array('action'));
$Crud
->expects($this->once())
->method('action')
->will($this->returnValue($Action));
$CrudSubject = new CrudSubject(array('request' => $Request, 'crud' => $Crud, 'controller' => $Controller));
$Instance = $this->getMock('ApiQueryLogListener', array('_getQueryLogs'), array($CrudSubject));
$Instance
->expects($this->once())
->method('_getQueryLogs');
$Instance->beforeRender(new CakeEvent('something'));
}
/**
* Check if get query logs method works as expected
*
* @covers ApiQueryLogListener::_getQueryLogs
* @return void
*/
public function testGetQueryLogs() {
// Implements getLog, should be called
$defaultSource = $this->getMock('stdClass', array('getLog'));
$defaultSource
->expects($this->once())
->method('getLog')
->with(false, false)
->will($this->returnValue(array()));
// Does not implement getLog, should not be called
$testSource = $this->getMock('stdClass', array());
$testSource
->expects($this->never())
->method('getLog');
$Instance = $this->getMock('ApiQueryLogListener', array('_getSources', '_getSource'), array(new CrudSubject()));
$Instance
->expects($this->at(0))
->method('_getSources')
->will($this->returnValue(array('default', 'test')));
$Instance
->expects($this->at(1))
->method('_getSource')
->with('default')
->will($this->returnValue($defaultSource));
$Instance
->expects($this->at(2))
->method('_getSource')
->with('test')
->will($this->returnValue($testSource));
$Method = new ReflectionMethod($Instance, '_getQueryLogs');
$Method->setAccessible(true);
$result = $Method->invoke($Instance);
$expected = array('default' => array());
$this->assertEquals($expected, $result);
}
}

View File

@ -1,529 +0,0 @@
<?php
App::uses('CakeRequest', 'Network');
App::uses('Model', 'Model');
App::uses('CrudTestCase', 'Crud.Test/Support');
App::uses('CrudSubject', 'Crud.Controller/Crud');
App::uses('RedirectListener', 'Crud.Controller/Crud/Listener');
/**
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class RedirectListenerTest extends CrudTestCase {
/**
* Test the correct events is bound
*
* @covers RedirectListener::implementedEvents
* @return void
*/
public function testImplementedEvents() {
$listener = $this
->getMockBuilder('RedirectListener')
->setMethods(null)
->disableoriginalConstructor()
->getMock();
$result = $listener->implementedEvents();
$expected = array(
'Crud.beforeRedirect' => array('callable' => 'beforeRedirect', 'priority' => 90)
);
$this->assertEquals($expected, $result);
}
/**
* Test that we got the default readers bound on setup
*
* @covers RedirectListener::setup
* @return void
*/
public function testSetup() {
$listener = $this
->getMockBuilder('RedirectListener')
->setMethods(null)
->disableoriginalConstructor()
->getMock();
$listener->setup();
$result = $listener->config('readers');
$result['request'] = array_keys($result['request']);
$result['model'] = array_keys($result['model']);
$result['subject'] = array_keys($result['subject']);
$expected = array(
'request' => array(
'key',
'data',
'query'
),
'model' => array(
'key',
'data',
'field'
),
'subject' => array(
'key'
)
);
$this->assertEquals($expected, $result);
}
/**
* Test getting an existing reader by name works
*
* @covers RedirectListener::reader
* @return void
*/
public function testReaderGetWorks() {
$listener = $this
->getMockBuilder('RedirectListener')
->setMethods(null)
->disableoriginalConstructor()
->getMock();
$listener->setup();
$closure = $listener->reader('request.key');
$this->assertNotNull($closure);
$this->assertInstanceOf('Closure', $closure);
}
/**
* Test getting a non-existing reader by name fails
*
* @covers RedirectListener::reader
* @return void
*/
public function testReaderGetFails() {
$listener = $this
->getMockBuilder('RedirectListener')
->setMethods(null)
->disableoriginalConstructor()
->getMock();
$listener->setup();
$closure = $listener->reader('something_invalid');
$this->assertNull($closure);
}
/**
* Test setting a reader by name works
*
* @covers RedirectListener::reader
* @return void
*/
public function testReaderSetWorks() {
$listener = $this
->getMockBuilder('RedirectListener')
->setMethods(null)
->disableoriginalConstructor()
->getMock();
$listener->setup();
$closure = function() {
};
$actual = $listener->reader('my.reader', $closure);
$this->assertSame($listener, $actual);
$actual = $listener->reader('my.reader');
$this->assertSame($closure, $actual);
}
/**
* Test the reader "request.key"
*
* @return void
*/
public function testReaderRequestKey() {
$listener = $this
->getMockBuilder('RedirectListener')
->setMethods(null)
->disableoriginalConstructor()
->getMock();
$listener->setup();
$subject = new CrudSubject();
$subject->request = new CakeRequest();
$subject->request->params['action'] = 'index';
$reader = $listener->reader('request.key');
$result = $reader($subject, 'action');
$this->assertEquals('index', $result);
$result = $reader($subject, 'something_wrong');
$this->assertNull($result);
}
/**
* Test the reader "request.data"
*
* @return void
*/
public function testReaderRequestData() {
$listener = $this
->getMockBuilder('RedirectListener')
->setMethods(null)
->disableoriginalConstructor()
->getMock();
$listener->setup();
$subject = new CrudSubject();
$subject->request = new CakeRequest();
$subject->request->data = array('hello' => 'world');
$reader = $listener->reader('request.data');
$result = $reader($subject, 'hello');
$this->assertEquals('world', $result);
$result = $reader($subject, 'something_wrong');
$this->assertNull($result);
}
/**
* Test the reader "request.query"
*
* @return void
*/
public function testReaderRequestQuery() {
$listener = $this
->getMockBuilder('RedirectListener')
->setMethods(null)
->disableoriginalConstructor()
->getMock();
$listener->setup();
$subject = new CrudSubject();
$subject->request = new CakeRequest();
$subject->request->query = array('hello' => 'world');
$reader = $listener->reader('request.query');
$result = $reader($subject, 'hello');
$this->assertEquals('world', $result);
$result = $reader($subject, 'something_wrong');
$this->assertNull($result);
}
/**
* Test the reader "model.key"
*
* @return void
*/
public function testReaderModelKey() {
$listener = $this
->getMockBuilder('RedirectListener')
->setMethods(null)
->disableoriginalConstructor()
->getMock();
$listener->setup();
$subject = new CrudSubject();
$subject->model = new Model();
$subject->model->data = array('hello' => 'world');
$reader = $listener->reader('model.key');
$result = $reader($subject, 'data');
$this->assertEquals(array('hello' => 'world'), $result);
$result = $reader($subject, 'something_wrong');
$this->assertNull($result);
}
/**
* Test the reader "model.data"
*
* @return void
*/
public function testReaderModelData() {
$listener = $this
->getMockBuilder('RedirectListener')
->setMethods(null)
->disableoriginalConstructor()
->getMock();
$listener->setup();
$subject = new CrudSubject();
$subject->model = new Model();
$subject->model->data = array('hello' => 'world');
$reader = $listener->reader('model.data');
$result = $reader($subject, 'hello');
$this->assertEquals('world', $result);
$result = $reader($subject, 'something_wrong');
$this->assertNull($result);
}
/**
* Test the reader "model.field"
*
* @return void
*/
public function testReaderModelField() {
$listener = $this
->getMockBuilder('RedirectListener')
->setMethods(null)
->disableoriginalConstructor()
->getMock();
$listener->setup();
$subject = new CrudSubject();
$subject->model = $this
->getMockBuilder('Model')
->setMethods(array('field'))
->disableoriginalConstructor()
->getMock();
$subject->model
->expects($this->once())
->method('field')
->with('slug')
->will($this->returnValue('ok-slug-is-ok'));
$reader = $listener->reader('model.field');
$result = $reader($subject, 'slug');
$this->assertEquals('ok-slug-is-ok', $result);
}
/**
* Test the reader "subject.key"
*
* @return void
*/
public function testReaderSubjectKey() {
$listener = $this
->getMockBuilder('RedirectListener')
->setMethods(null)
->disableoriginalConstructor()
->getMock();
$listener->setup();
$subject = new CrudSubject();
$subject->welcome = 'hello world';
$reader = $listener->reader('subject.key');
$result = $reader($subject, 'welcome');
$this->assertEquals('hello world', $result);
$result = $reader($subject, 'something_invalid');
$this->assertNull($result);
}
/**
* Test how `redirect` handles an action without any
* redirect configuration
*
* @covers RedirectListener::beforeRedirect
* @return void
*/
public function testRedirectWithNoConfig() {
$action = $this
->getMockBuilder('CrudAction')
->setMethods(null)
->disableoriginalConstructor()
->getMock();
$listener = $this
->getMockBuilder('RedirectListener')
->setMethods(array('_action', '_getKey'))
->disableoriginalConstructor()
->getMock();
$listener
->expects($this->once())
->method('_action')
->will($this->returnValue($action));
$listener
->expects($this->never())
->method('_getKey');
$subject = new CrudSubject();
$listener->beforeRedirect(new CakeEvent('Crud.beforeRedirect', $subject));
}
/**
* Test how `redirect` handles an action with action redirect
* configuration
*
* @covers RedirectListener::beforeRedirect
* @return void
*/
public function testRedirectWithConfigButNoValidKey() {
$action = $this
->getMockBuilder('CrudAction')
->setMethods(null)
->disableoriginalConstructor()
->getMock();
$action->redirectConfig('add', array('reader' => 'request.key', 'key' => 'hello'));
$subject = new CrudSubject();
$listener = $this
->getMockBuilder('RedirectListener')
->setMethods(array('_action', '_getKey', '_getUrl'))
->disableoriginalConstructor()
->getMock();
$listener
->expects($this->once())
->method('_action')
->will($this->returnValue($action));
$listener
->expects($this->once())
->method('_getKey')
->with($subject, 'request.key', 'hello')
->will($this->returnValue(false));
$listener
->expects($this->never())
->method('_getUrl');
$listener->beforeRedirect(new CakeEvent('Crud.beforeRedirect', $subject));
}
/**
* Test how `redirect` handles an action with action redirect
* configuration
*
* @covers RedirectListener::beforeRedirect
* @return void
*/
public function testRedirectWithConfigAndValidKey() {
$action = $this
->getMockBuilder('CrudAction')
->setMethods(null)
->disableoriginalConstructor()
->getMock();
$action->redirectConfig('add', array(
'reader' => 'request.key',
'key' => 'hello',
'url' => array('action' => 'index')
));
$subject = new CrudSubject();
$listener = $this
->getMockBuilder('RedirectListener')
->setMethods(array('_action', '_getKey', '_getUrl'))
->disableoriginalConstructor()
->getMock();
$listener
->expects($this->once())
->method('_action')
->will($this->returnValue($action));
$listener
->expects($this->once())
->method('_getKey')
->with($subject, 'request.key', 'hello')
->will($this->returnValue(true));
$listener
->expects($this->once())
->method('_getUrl')
->with($subject, array('action' => 'index'))
->will($this->returnValue(array('action' => 'index')));
$listener->beforeRedirect(new CakeEvent('Crud.beforeRedirect', $subject));
$this->assertSame(array('action' => 'index'), $subject->url);
}
public function dataProvider_getUrl() {
$CakeRequest = new CakeRequest;
$CakeRequest->params['action'] = 'index';
$CakeRequest->query['parent_id'] = 10;
$CakeRequest->data['epic'] = 'jippi';
$Model = new Model;
$Model->id = 69;
$Model->slug = 'jippi-is-awesome';
$Model->data = array('name' => 'epic', 'slug' => 'epic');
return array(
array(
new CrudSubject(),
array('action' => 'index'),
array('action' => 'index')
),
array(
new CrudSubject(),
array('controller' => 'posts', 'action' => 'index'),
array('controller' => 'posts', 'action' => 'index')
),
array(
new CrudSubject(array('request' => $CakeRequest)),
array('action' => array('request.key', 'action')),
array('action' => 'index')
),
array(
new CrudSubject(array('request' => $CakeRequest)),
array('action' => array('request.data', 'epic')),
array('action' => 'jippi')
),
array(
new CrudSubject(array('request' => $CakeRequest)),
array('action' => array('request.query', 'parent_id')),
array('action' => 10)
),
array(
new CrudSubject(array('model' => $Model)),
array('action' => 'edit', array('model.key', 'id')),
array('action' => 'edit', 69)
),
array(
new CrudSubject(array('model' => $Model)),
array('action' => 'edit', array('model.data', 'slug')),
array('action' => 'edit', 'epic')
),
array(
new CrudSubject(array('model' => $Model)),
array('action' => 'edit', '?' => array('name' => array('model.data', 'slug'))),
array('action' => 'edit', '?' => array('name' => 'epic'))
),
array(
new CrudSubject(array('id' => 69)),
array('action' => 'edit', array('subject.key', 'id')),
array('action' => 'edit', 69)
)
);
}
/**
* Test _getUrl
*
* @dataProvider dataProvider_getUrl
* @covers RedirectListener::_getUrl
* @covers RedirectListener::_getKey
* @return void
*/
public function test_getUrl(CrudSubject $subject, $url, $expected) {
$listener = $this
->getMockBuilder('RedirectListener')
->setMethods(null)
->disableoriginalConstructor()
->getMock();
$listener->setup();
$this->setReflectionClassInstance($listener);
$result = $this->callProtectedMethod('_getUrl', array($subject, $url), $listener);
$this->assertEquals($expected, $result);
}
}

View File

@ -1,826 +0,0 @@
<?php
App::uses('Model', 'Model');
App::uses('Controller', 'Controller');
App::uses('TreeBehavior', 'Model/Behavior');
App::uses('CrudAction', 'Crud.Controller/Crud');
App::uses('RelatedModelsListener', 'Crud.Controller/Crud/Listener');
App::uses('CrudSubject', 'Crud.Controller/Crud');
App::uses('CrudTestCase', 'Crud.Test/Support');
/**
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class RelatedModelListenerTest extends CrudTestCase {
/**
* testModels
*
* @covers RelatedModelsListener::models
* @return void
*/
public function testModels() {
$Action = $this
->getMockBuilder('CrudAction')
->disableOriginalConstructor()
->setMethods(array('_handle'))
->getMock();
$Action->config('relatedModels', array('Post', 'User'));
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array('_action'))
->getMock();
$Listener
->expects($this->once())
->method('_action')
->with(null)
->will($this->returnValue($Action));
$result = $Listener->models();
$expected = array('Post', 'User');
$this->assertEquals($expected, $result);
}
/**
* testModelsEmpty
*
* Test behavior when 'relatedModels' is empty
*
* @covers RelatedModelsListener::models
* @return void
*/
public function testModelsEmpty() {
$Action = $this
->getMockBuilder('CrudAction')
->disableOriginalConstructor()
->setMethods(array('_handle'))
->getMock();
$Action->config('relatedModels', null);
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array('_action'))
->getMock();
$Listener
->expects($this->once())
->method('_action')
->with(null)
->will($this->returnValue($Action));
$result = $Listener->models();
$expected = array();
$this->assertEquals($expected, $result);
}
/**
* testModelsEmpty
*
* Test behavior when 'relatedModels' is a string
*
* @covers RelatedModelsListener::models
* @return void
*/
public function testModelsString() {
$Action = $this
->getMockBuilder('CrudAction')
->disableOriginalConstructor()
->setMethods(array('_handle'))
->getMock();
$Action->config('relatedModels', 'Post');
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array('_action'))
->getMock();
$Listener
->expects($this->once())
->method('_action')
->with(null)
->will($this->returnValue($Action));
$result = $Listener->models();
$expected = array('Post');
$this->assertEquals($expected, $result);
}
/**
* testModelsTrue
*
* Test behavior when 'relatedModels' is true
*
* @covers RelatedModelsListener::models
* @return void
*/
public function testModelsTrue() {
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->setMethods(array('getAssociated'))
->getMock();
$Action = $this
->getMockBuilder('CrudAction')
->disableOriginalConstructor()
->setMethods(array('_handle'))
->getMock();
$Action->config('relatedModels', true);
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array('_action', '_model'))
->getMock();
$Listener
->expects($this->once())
->method('_action')
->with(null)
->will($this->returnValue($Action));
$Listener
->expects($this->once())
->method('_model')
->with()
->will($this->returnValue($Model));
$Model
->expects($this->at(0))
->method('getAssociated')
->with('belongsTo')
->will($this->returnValue(array('Post')));
$Model
->expects($this->at(1))
->method('getAssociated')
->with('hasAndBelongsToMany')
->will($this->returnValue(array('Tag')));
$result = $Listener->models();
$expected = array('Post', 'Tag');
$this->assertEquals($expected, $result);
}
/**
* test_findRelatedItems
*
* Test behavior for a model without a TreeBehavior
*
* @covers RelatedModelsListener::_findRelatedItems
* @return void
*/
public function test_findRelatedItems() {
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->setMethods(array('find'))
->getMock();
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array('_hasTreeBehavior'))
->getMock();
$query = array(
'conditions' => array('Model.is_active' => true)
);
$data = array(
array('Model' => array('id' => 1))
);
$Listener
->expects($this->once())
->method('_hasTreeBehavior')
->with($Model)
->will($this->returnValue(false));
$Model
->expects($this->once())
->method('find')
->with('list', $query)
->will($this->returnValue($data));
$this->setReflectionClassInstance($Listener);
$result = $this->callProtectedMethod('_findRelatedItems', array($Model, $query), $Listener);
$expected = $data;
$this->assertEquals($expected, $result);
}
/**
* test_findRelatedItemsTreeBehavior
*
* Test behavior for a model with TreeBehavior
*
* @covers RelatedModelsListener::_findRelatedItems
* @return void
*/
public function test_findRelatedItemsTreeBehavior() {
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->setMethods(array('generateTreeList'))
->getMock();
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array('_hasTreeBehavior'))
->getMock();
$query = array(
'conditions' => array(),
'keyPath' => 'id',
'valuePath' => 'name',
'spacer' => '_',
'recursive' => -1
);
$data = array(
array('Model' => array('id' => 1))
);
$Listener
->expects($this->once())
->method('_hasTreeBehavior')
->with($Model)
->will($this->returnValue(true));
$Model
->expects($this->once())
->method('generateTreeList')
->with(array(), 'id', 'name', '_', -1)
->will($this->returnValue($data));
$this->setReflectionClassInstance($Listener);
$result = $this->callProtectedMethod('_findRelatedItems', array($Model, $query), $Listener);
$expected = $data;
$this->assertEquals($expected, $result);
}
/**
* test_getAssociationType
*
* @covers RelatedModelsListener::_getAssociationType
* @return void
*/
public function test_getAssociationType() {
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->setMethods(array('getAssociated'))
->getMock();
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array('_model'))
->getMock();
$Listener
->expects($this->any())
->method('_model')
->with()
->will($this->returnValue($Model));
$Model
->expects($this->any())
->method('getAssociated')
->with()
->will($this->returnValue(array('Post' => 'belongsTo', 'Tag' => 'hasMany')));
$this->setReflectionClassInstance($Listener);
$result = $this->callProtectedMethod('_getAssociationType', array('Post'), $Listener);
$expected = 'belongsTo';
$this->assertEquals($expected, $result);
$result = $this->callProtectedMethod('_getAssociationType', array('Tag'), $Listener);
$expected = 'hasMany';
$this->assertEquals($expected, $result);
$result = $this->callProtectedMethod('_getAssociationType', array('Comment'), $Listener);
$expected = null;
$this->assertEquals($expected, $result);
}
/**
* test_getModelInstance
*
* Test that the associated model exist in the Primary Model
*
* @covers RelatedModelsListener::_getModelInstance
* @return void
*/
public function test_getModelInstance() {
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$Model->Post = 'PostModel';
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array('_model', '_controller'))
->getMock();
$Listener
->expects($this->once())
->method('_model')
->with()
->will($this->returnValue($Model));
$Listener
->expects($this->never())
->method('_controller');
$this->setReflectionClassInstance($Listener);
$result = $this->callProtectedMethod('_getModelInstance', array('Post'), $Listener);
$expected = 'PostModel';
$this->assertEquals($expected, $result);
}
/**
* test_getModelInstanceThroughController
*
* Get the model from the controller
*
* @covers RelatedModelsListener::_getModelInstance
* @return void
*/
public function test_getModelInstanceThroughController() {
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$PostModel = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$Controller = $this
->getMockBuilder('Controller')
->disableOriginalConstructor()
->setMethods(array('food'))
->getMock();
$Controller->Post = $PostModel;
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array('_model', '_controller', '_classRegistryInit'))
->getMock();
$Listener
->expects($this->once())
->method('_model')
->with()
->will($this->returnValue($Model));
$Listener
->expects($this->once())
->method('_controller')
->with()
->will($this->returnValue($Controller));
$Listener
->expects($this->never())
->method('_classRegistryInit');
$this->setReflectionClassInstance($Listener);
$result = $this->callProtectedMethod('_getModelInstance', array('Post'), $Listener);
$expected = $PostModel;
$this->assertEquals($expected, $result);
}
/**
* test_getModelInstanceThroughModelAssociation
*
* Get the model through ClassRegistry from associated
* model className
*
* @covers RelatedModelsListener::_getModelInstance
* @return void
*/
public function test_getModelInstanceThroughModelAssociation() {
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$Model->belongsTo = array(
'Post' => array(
'className' => 'MyPlugin.Post'
)
);
$PostModel = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$Controller = $this
->getMockBuilder('Controller')
->disableOriginalConstructor()
->setMethods(array('food'))
->getMock();
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array('_model', '_controller', '_classRegistryInit'))
->getMock();
$Listener
->expects($this->once())
->method('_model')
->with()
->will($this->returnValue($Model));
$Listener
->expects($this->once())
->method('_controller')
->with()
->will($this->returnValue($Controller));
$Listener
->expects($this->once())
->method('_classRegistryInit')
->with('MyPlugin.Post')
->will($this->returnValue($PostModel));
$this->setReflectionClassInstance($Listener);
$result = $this->callProtectedMethod('_getModelInstance', array('Post', 'belongsTo'), $Listener);
$expected = $PostModel;
$this->assertEquals($expected, $result);
}
/**
* test_getModelInstanceThroughClassRegistry
*
* Get the model directly from ClassRegistry
*
* @covers RelatedModelsListener::_getModelInstance
* @return void
*/
public function test_getModelInstanceThroughClassRegistry() {
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$PostModel = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$Controller = $this
->getMockBuilder('Controller')
->disableOriginalConstructor()
->setMethods(array('food'))
->getMock();
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array('_model', '_controller', '_classRegistryInit'))
->getMock();
$Listener
->expects($this->once())
->method('_model')
->with()
->will($this->returnValue($Model));
$Listener
->expects($this->once())
->method('_controller')
->with()
->will($this->returnValue($Controller));
$Listener
->expects($this->once())
->method('_classRegistryInit')
->with('Post')
->will($this->returnValue($PostModel));
$this->setReflectionClassInstance($Listener);
$result = $this->callProtectedMethod('_getModelInstance', array('Post'), $Listener);
$expected = $PostModel;
$this->assertEquals($expected, $result);
}
/**
* test_getBaseQuery
*
* Test a belongsTo relation
*
* @covers RelatedModelsListener::_getBaseQuery
* @return void
*/
public function test_getBaseQuery() {
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$Model->belongsTo = array('Post' => array('conditions' => array('is_active' => true)));
$Associated = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$Associated->alias = 'Post';
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array('_model', '_hasTreeBehavior'))
->getMock();
$Listener
->expects($this->once())
->method('_model')
->with()
->will($this->returnValue($Model));
$Listener
->expects($this->once())
->method('_hasTreeBehavior')
->with($Associated)
->will($this->returnValue(false));
$this->setReflectionClassInstance($Listener);
$result = $this->callProtectedMethod('_getBaseQuery', array($Associated, 'belongsTo'), $Listener);
$expected = array('conditions' => array(array('is_active' => true)));
$this->assertEquals($expected, $result);
}
/**
* test_getBaseQueryHasMany
*
* Test a hasMany relation that no conditions
* will be added by default
*
* @covers RelatedModelsListener::_getBaseQuery
* @return void
*/
public function test_getBaseQueryHasMany() {
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$Associated = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$Associated->alias = 'Post';
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array('_model', '_hasTreeBehavior'))
->getMock();
$Listener
->expects($this->never())
->method('_model');
$Listener
->expects($this->once())
->method('_hasTreeBehavior')
->with($Associated)
->will($this->returnValue(false));
$this->setReflectionClassInstance($Listener);
$result = $this->callProtectedMethod('_getBaseQuery', array($Associated, 'hasMany'), $Listener);
$expected = array();
$this->assertEquals($expected, $result);
}
/**
* test_getBaseQueryTreeBehavior
*
* Test a relation where associated model has
* TreeBehavior bound
*
* @covers RelatedModelsListener::_getBaseQuery
* @return void
*/
public function test_getBaseQueryTreeBehavior() {
$Model = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$Associated = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$Associated->alias = 'Post';
$Behavior = $this
->getMockBuilder('TreeBehavior')
->disableOriginalConstructor()
->getMock();
$Behavior->settings['Post'] = array(
'recursive' => -1,
'scope' => array('is_active' => true)
);
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array('_model', '_hasTreeBehavior', '_getTreeBehavior'))
->getMock();
$Listener
->expects($this->never())
->method('_model');
$Listener
->expects($this->once())
->method('_hasTreeBehavior')
->with($Associated)
->will($this->returnValue(true));
$Listener
->expects($this->once())
->method('_getTreeBehavior')
->with($Associated)
->will($this->returnValue($Behavior));
$this->setReflectionClassInstance($Listener);
$result = $this->callProtectedMethod('_getBaseQuery', array($Associated), $Listener);
$expected = array(
'keyPath' => null,
'valuePath' => null,
'spacer' => '_',
'recursive' => -1,
'conditions' => array(array('is_active' => true))
);
$this->assertEquals($expected, $result);
}
/**
* testPublishRelatedModels
*
* @covers RelatedModelsListener::publishRelatedModels
* @return void
*/
public function testPublishRelatedModels() {
$Controller = $this
->getMockBuilder('Controller')
->disableOriginalConstructor()
->setMethods(array('set'))
->getMock();
$Post = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$Post->alias = 'Post';
$postQuery = array('conditions' => array('is_active' => true));
$i = 0;
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array(
'models', '_controller', '_getAssociationType', '_getModelInstance',
'_getBaseQuery', '_trigger', '_findRelatedItems'
))
->getMock();
$Listener
->expects($this->at($i++))
->method('models')
->with(null)
->will($this->returnValue(array('Post')));
$Listener
->expects($this->at($i++))
->method('_controller')
->with()
->will($this->returnValue($Controller));
$Listener
->expects($this->at($i++))
->method('_getAssociationType')
->with('Post')
->will($this->returnValue('belongsTo'));
$Listener
->expects($this->at($i++))
->method('_getModelInstance')
->with('Post', 'belongsTo')
->will($this->returnValue($Post));
$Listener
->expects($this->at($i++))
->method('_getBaseQuery')
->with($Post, 'belongsTo')
->will($this->returnValue($postQuery));
$Listener
->expects($this->at($i++))
->method('_trigger')
->with('beforeRelatedModel', array('modelName' => 'Post', 'query' => $postQuery, 'viewVar' => 'posts', 'associationType' => 'belongsTo', 'associatedModel' => $Post))
->will($this->returnValue(new CrudSubject(array('query' => $postQuery + array('_callback' => true)))));
$Listener
->expects($this->at($i++))
->method('_findRelatedItems')
->with($Post, $postQuery + array('_callback' => true))
->will($this->returnValue(array(1, 2, 3)));
$Listener
->expects($this->at($i++))
->method('_trigger')
->with('afterRelatedModel', array('modelName' => 'Post', 'items' => array(1, 2, 3), 'viewVar' => 'posts', 'associationType' => 'belongsTo', 'associatedModel' => $Post))
->will($this->returnValue(new CrudSubject(array('items' => array(1, 2, 3), 'viewVar' => 'posts'))));
$Controller
->expects($this->once())
->method('set')
->with('posts', array(1, 2, 3));
$Listener->publishRelatedModels();
}
/**
* testPublishRelatedModelsNoModels
*
* Test that nothing happens if the related models
* array is empty
*
* @covers RelatedModelsListener::publishRelatedModels
* @return void
*/
public function testPublishRelatedModelsNoModels() {
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array('models', '_controller'))
->getMock();
$Listener
->expects($this->once())
->method('models')
->with(null)
->will($this->returnValue(false));
$Listener
->expects($this->never())
->method('_controller');
$Listener->publishRelatedModels();
}
/**
* testPublishRelatedModelsViewVarExists
*
* Test that nothing will be done if the related models
* viewVar already exists in Controller::$viewVars
*
* @covers RelatedModelsListener::publishRelatedModels
* @return void
*/
public function testPublishRelatedModelsViewVarExists() {
$Controller = $this
->getMockBuilder('Controller')
->disableOriginalConstructor()
->setMethods(array('set'))
->getMock();
$Controller->viewVars['posts'] = array(1, 2, 3);
$Post = $this
->getMockBuilder('Model')
->disableOriginalConstructor()
->getMock();
$Post->alias = 'Post';
$postQuery = array('conditions' => array('is_active' => true));
$i = 0;
$Listener = $this
->getMockBuilder('RelatedModelsListener')
->disableOriginalConstructor()
->setMethods(array(
'models', '_controller', '_getAssociationType', '_getModelInstance',
'_getBaseQuery', '_trigger', '_findRelatedItems'
))
->getMock();
$Listener
->expects($this->at($i++))
->method('models')
->with(null)
->will($this->returnValue(array('Post')));
$Listener
->expects($this->at($i++))
->method('_controller')
->with()
->will($this->returnValue($Controller));
$Listener
->expects($this->at($i++))
->method('_getAssociationType')
->with('Post')
->will($this->returnValue('belongsTo'));
$Listener
->expects($this->at($i++))
->method('_getModelInstance')
->with('Post', 'belongsTo')
->will($this->returnValue($Post));
$Listener
->expects($this->never())
->method('_getBaseQuery');
$Listener
->expects($this->never())
->method('_trigger');
$Listener
->expects($this->never())
->method('_findRelatedItems');
$Controller
->expects($this->never())
->method('set');
$Listener->publishRelatedModels();
}
}

View File

@ -1,323 +0,0 @@
<?php
App::uses('CakeEvent', 'Event');
App::uses('Controller', 'Controller');
App::uses('CrudSubject', 'Crud.Controller/Crud');
App::uses('ScaffoldListener', 'Crud.Controller/Crud/Listener');
require_once CAKE . DS . 'Test' . DS . 'Case' . DS . 'Model' . DS . 'models.php';
/**
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class ScaffoldListenerTest extends CakeTestCase {
/**
* fixtures property
*
* @var array
*/
public $fixtures = array(
'core.article',
'core.user',
'core.comment',
'core.join_thing',
'core.tag',
'core.attachment'
);
/**
* Data used for beforeRenderProvider to setup
* the tests and environments
*
* @var array
*/
protected $_beforeRenderTests = array(
// Index (Article)
array(
'model' => 'Article',
'action' => 'index',
'controller' => 'ArticlesController',
'expected' => array(
'title_for_layout' => 'Scaffold :: Index :: ',
'modelClass' => 'Article',
'primaryKey' => 'id',
'displayField' => 'title',
'singularVar' => 'article',
'pluralVar' => 'articlesController',
'singularHumanName' => 'Article',
'pluralHumanName' => 'Articles Controller',
'scaffoldFields' => array(
'id', 'user_id', 'title', 'body', 'published', 'created', 'updated'
),
'associations' => array(
'belongsTo' => array(
'User' => array(
'primaryKey' => 'id',
'displayField' => 'id',
'foreignKey' => 'user_id',
'plugin' => null,
'controller' => 'users'
),
),
'hasMany' => array(
'Comment' => array(
'primaryKey' => 'id',
'displayField' => 'id',
'foreignKey' => 'article_id',
'plugin' => null,
'controller' => 'comments'
)
),
'hasAndBelongsToMany' => array(
'Tag' => array(
'primaryKey' => 'id',
'displayField' => 'id',
'foreignKey' => 'article_id',
'plugin' => null,
'controller' => 'tags',
'with' => 'ArticlesTag',
)
)
)
)
),
// Add (Article)
array(
'model' => 'Article',
'action' => 'add',
'controller' => 'ArticlesController',
'expected' => array(
'title_for_layout' => 'Scaffold :: Add :: ',
'modelClass' => 'Article',
'primaryKey' => 'id',
'displayField' => 'title',
'singularVar' => 'article',
'pluralVar' => 'articlesController',
'singularHumanName' => 'Article',
'pluralHumanName' => 'Articles Controller',
'scaffoldFields' => array(
'id', 'user_id', 'title', 'body', 'published', 'created', 'updated'
),
'associations' => array(
'belongsTo' => array(
'User' => array(
'primaryKey' => 'id',
'displayField' => 'id',
'foreignKey' => 'user_id',
'plugin' => null,
'controller' => 'users'
),
),
'hasMany' => array(
'Comment' => array(
'primaryKey' => 'id',
'displayField' => 'id',
'foreignKey' => 'article_id',
'plugin' => null,
'controller' => 'comments'
)
),
'hasAndBelongsToMany' => array(
'Tag' => array(
'primaryKey' => 'id',
'displayField' => 'id',
'foreignKey' => 'article_id',
'plugin' => null,
'controller' => 'tags',
'with' => 'ArticlesTag',
)
)
)
)
),
// Edit (Article)
array(
'model' => 'Article',
'action' => 'edit',
'controller' => 'ArticlesController',
'expected' => array(
'title_for_layout' => 'Scaffold :: Edit :: ',
'modelClass' => 'Article',
'primaryKey' => 'id',
'displayField' => 'title',
'singularVar' => 'article',
'pluralVar' => 'articlesController',
'singularHumanName' => 'Article',
'pluralHumanName' => 'Articles Controller',
'scaffoldFields' => array(
'id', 'user_id', 'title', 'body', 'published', 'created', 'updated'
),
'associations' => array(
'belongsTo' => array(
'User' => array(
'primaryKey' => 'id',
'displayField' => 'id',
'foreignKey' => 'user_id',
'plugin' => null,
'controller' => 'users'
),
),
'hasMany' => array(
'Comment' => array(
'primaryKey' => 'id',
'displayField' => 'id',
'foreignKey' => 'article_id',
'plugin' => null,
'controller' => 'comments'
)
),
'hasAndBelongsToMany' => array(
'Tag' => array(
'primaryKey' => 'id',
'displayField' => 'id',
'foreignKey' => 'article_id',
'plugin' => null,
'controller' => 'tags',
'with' => 'ArticlesTag',
)
)
)
)
),
// Index (User)
array(
'model' => 'User',
'action' => 'index',
'controller' => 'UsersController',
'expected' => array(
'title_for_layout' => 'Scaffold :: Index :: ',
'modelClass' => 'User',
'primaryKey' => 'id',
'displayField' => 'id',
'singularVar' => 'user',
'pluralVar' => 'usersController',
'singularHumanName' => 'User',
'pluralHumanName' => 'Users Controller',
'scaffoldFields' => array(
'id', 'user', 'password', 'created', 'updated'
),
'associations' => array(
)
)
),
// Index (Comment)
array(
'model' => 'Comment',
'action' => 'index',
'controller' => 'CommentsController',
'expected' => array(
'title_for_layout' => 'Scaffold :: Index :: ',
'modelClass' => 'Comment',
'primaryKey' => 'id',
'displayField' => 'id',
'singularVar' => 'comment',
'pluralVar' => 'commentsController',
'singularHumanName' => 'Comment',
'pluralHumanName' => 'Comments Controller',
'scaffoldFields' => array(
'id', 'article_id', 'user_id', 'comment', 'published', 'created', 'updated'
),
'associations' => array(
'belongsTo' => array(
'User' => array(
'primaryKey' => 'id',
'displayField' => 'id',
'foreignKey' => 'user_id',
'plugin' => null,
'controller' => 'users'
),
'Article' => array(
'primaryKey' => 'id',
'displayField' => 'title',
'foreignKey' => 'article_id',
'plugin' => null,
'controller' => 'articles'
)
),
'hasOne' => array(
'Attachment' => array(
'primaryKey' => 'id',
'displayField' => 'id',
'foreignKey' => 'comment_id',
'plugin' => null,
'controller' => 'attachments'
)
)
)
)
)
);
/**
* Data provider for testBeforeRender
*
* Setup the required classes and their
* relations
*
* @return array
*/
public function beforeRenderProvider() {
$data = array();
foreach ($this->_beforeRenderTests as $test) {
$Request = new CakeRequest(null, false);
$Request->action = $test['action'];
$Controller = new Controller($Request);
$Controller->name = $test['controller'];
$Controller->modelClass = $test['model'];
$Model = new $test['model']();
$Subject = new CrudSubject();
$Subject->model = $Model;
$Subject->request = $Request;
$Subject->controller = $Controller;
$Event = new CakeEvent('Crud.beforeRender', $Subject);
$Listener = $this->getMock('ScaffoldListener', null, array($Subject));
$data[] = array($Listener, $Event, $test['expected']);
}
return $data;
}
/**
* test that the proper names and variable values are set by Scaffold
*
* @dataProvider beforeRenderProvider
* @param CrudListener $Listener
* @param CakeEvent $Event
* @param array $expected
* @return void
*/
public function testBeforeRender($Listener, $Event, $expected) {
$Listener->beforeRender($Event);
$this->assertEquals($expected, $Event->subject->controller->viewVars);
}
/**
* Test that implementedEvents return the correct events
*
* @return void
*/
public function testImplementedEvents() {
$Subject = new CrudSubject();
$Listener = $this->getMock('ScaffoldListener', null, array($Subject));
$expected = array(
'Crud.beforeRender' => 'beforeRender',
'Crud.beforeFind' => 'beforeFind',
'Crud.beforePaginate' => 'beforePaginate'
);
$result = $Listener->implementedEvents();
$this->assertEquals($expected, $result);
}
}

View File

@ -1,719 +0,0 @@
<?php
App::uses('Controller', 'Controller');
App::uses('Component', 'Controller');
App::uses('ComponentCollection', 'Controller');
App::uses('Behavior', 'Model');
App::uses('BehaviorCollection', 'Model');
App::uses('CakeEvent', 'Event');
App::uses('CakeRequest', 'Network');
App::uses('CrudSubject', 'Crud.Controller/Crud');
App::uses('SearchListener', 'Crud.Controller/Crud/Listener');
/**
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class SearchListenerTest extends CakeTestCase {
public function setup() {
parent::setup();
if (!CakePlugin::loaded('Search')) {
try {
CakePlugin::load('Search');
} catch (MissingPluginException $e) {
$this->markTestSkipped('Search plugin not available');
}
}
}
/**
* Test implemented events
*
* @covers SearchListener::implementedEvents
* @return void
*/
public function testImplementedEvents() {
$Instance = new SearchListener(new CrudSubject());
$result = $Instance->implementedEvents();
$expected = array(
'Crud.beforeHandle' => array('callable' => 'beforeHandle', 'priority' => 50),
'Crud.beforePaginate' => array('callable' => 'beforePaginate', 'priority' => 50)
);
$this->assertEquals($expected, $result);
}
/**
* Test that scope returns instance of it self for chaining
*
* @covers SearchListener::scope
* @return void
*/
public function testScopeReturnsSelf() {
$Instance = new SearchListener(new CrudSubject());
$result = $Instance->scope('test', array('key' => 'value'));
$this->assertTrue($Instance === $result);
}
/**
* Test that scope without filter works
*
* @covers SearchListener::scope
* @return void
*/
public function testScopeWithoutFilter() {
$Instance = new SearchListener(new CrudSubject());
$Instance->scope('test', array('key' => 'value'));
$expected = array('query' => array('key' => 'value'), 'filter' => null);
$result = $Instance->config('scope.test');
$this->assertEquals($expected, $result);
}
/**
* Test that scope with filter works
*
* @covers SearchListener::scope
* @return void
*/
public function testScopeWithFilter() {
$Instance = new SearchListener(new CrudSubject());
$Instance->scope('test', array('key' => 'value'), array('epic' => 'value'));
$expected = array('query' => array('key' => 'value'), 'filter' => array('epic' => 'value'));
$result = $Instance->config('scope.test');
$this->assertEquals($expected, $result);
}
/**
* Test beforePaginate
*
* All clean, no configuration and nothing loaded
*
* @covers SearchListener::beforePaginate
* @return void
*/
public function testBeforePaginate() {
$Action = $this->getMock('stdClass', array('config'));
$Crud = $this->getMock('stdClass', array('action'));
$Crud
->expects($this->once())
->method('action')
->will($this->returnValue($Action));
$Model = new Model();
$Request = new CakeRequest();
$Controller = new Controller();
$CrudSubject = new CrudSubject(array(
'request' => $Request,
'crud' => $Crud,
'controller' => $Controller,
'model' => $Model
));
$mocked = array(
'_checkRequiredPlugin',
'_ensureComponent',
'_ensureBehavior',
'_commonProcess',
'_setFilterArgs',
'_setPaginationOptions'
);
$Instance = $this->getMock('SearchListener', $mocked, array($CrudSubject));
$Instance
->expects($this->once())
->method('_checkRequiredPlugin');
$Instance
->expects($this->once())
->method('_ensureComponent')
->with($Controller);
$Instance
->expects($this->once())
->method('_ensureBehavior')
->with($Model);
$Instance
->expects($this->once())
->method('_commonProcess')
->with($Controller, 'Model');
$Instance
->expects($this->once())
->method('_setFilterArgs')
->with($Model, array());
$Instance
->expects($this->once())
->method('_setPaginationOptions')
->with($Controller, $Model, array());
$Instance->beforePaginate(new CakeEvent('beforePaginate', $CrudSubject));
}
/**
* Test beforePaginate
*
* All clean, no configuration and nothing loaded
*
* @covers SearchListener::beforePaginate
* @return void
*/
public function testBeforePaginateWithModelFilterArgs() {
$Action = $this->getMock('stdClass', array('config'));
$Crud = $this->getMock('stdClass', array('action'));
$Crud
->expects($this->once())
->method('action')
->will($this->returnValue($Action));
$Model = new Model();
$Model->filterArgs = array('sample' => 'test');
$Request = new CakeRequest();
$Controller = new Controller();
$CrudSubject = new CrudSubject(array(
'request' => $Request,
'crud' => $Crud,
'controller' => $Controller,
'model' => $Model
));
$mocked = array(
'_checkRequiredPlugin',
'_ensureComponent',
'_ensureBehavior',
'_commonProcess',
'_setFilterArgs',
'_setPaginationOptions'
);
$Instance = $this->getMock('SearchListener', $mocked, array($CrudSubject));
$Instance
->expects($this->once())
->method('_checkRequiredPlugin');
$Instance
->expects($this->once())
->method('_ensureComponent')
->with($Controller);
$Instance
->expects($this->once())
->method('_ensureBehavior')
->with($Model);
$Instance
->expects($this->once())
->method('_commonProcess')
->with($Controller, 'Model');
$Instance
->expects($this->never())
->method('_setFilterArgs', 'Should not be called when model got filterArgs already');
$Instance
->expects($this->once())
->method('_setPaginationOptions')
->with($Controller, $Model, array());
$Instance->beforePaginate(new CakeEvent('beforePaginate', $CrudSubject));
}
/**
* Test beforePaginate
*
* Test that query scope works without a defined
* query scope in the listener
*
* @covers SearchListener::beforePaginate
* @return void
*/
public function testBeforePaginateWithUndefinedQueryScope() {
$Action = $this->getMock('stdClass', array('config'));
$Crud = $this->getMock('stdClass', array('action'));
$Crud
->expects($this->once())
->method('action')
->will($this->returnValue($Action));
$Model = new Model();
$Model->filterArgs = array('sample' => 'test');
$Request = new CakeRequest();
$Request->query['_scope'] = 'sample';
$Controller = new Controller();
$CrudSubject = new CrudSubject(array(
'request' => $Request,
'crud' => $Crud,
'controller' => $Controller,
'model' => $Model
));
$mocked = array(
'_checkRequiredPlugin',
'_ensureComponent',
'_ensureBehavior',
'_commonProcess',
'_setFilterArgs',
'_setPaginationOptions'
);
$Instance = $this->getMock('SearchListener', $mocked, array($CrudSubject));
$Instance
->expects($this->once())
->method('_checkRequiredPlugin');
$Instance
->expects($this->once())
->method('_ensureComponent')
->with($Controller);
$Instance
->expects($this->once())
->method('_ensureBehavior')
->with($Model);
$Instance
->expects($this->once())
->method('_commonProcess')
->with($Controller, 'Model');
$Instance
->expects($this->never())
->method('_setFilterArgs', 'Should not be called when model got filterArgs already');
$Instance
->expects($this->once())
->method('_setPaginationOptions')
->with($Controller, $Model, null);
$Instance->beforePaginate(new CakeEvent('beforePaginate', $CrudSubject));
}
/**
* Test beforePaginate
*
* Test that query scope works with a defined
* query scope in the listener
*
* @covers SearchListener::beforePaginate
* @return void
*/
public function testBeforePaginateWithDefinedQueryScope() {
$Model = new Model();
$Model->filterArgs = array('sample' => 'test');
$Request = new CakeRequest();
$Request->query['_scope'] = 'sample';
$Controller = new Controller();
$CrudSubject = new CrudSubject(array(
'request' => $Request,
'controller' => $Controller,
'model' => $Model
));
$mocked = array(
'_checkRequiredPlugin',
'_ensureComponent',
'_ensureBehavior',
'_commonProcess',
'_setFilterArgs',
'_setPaginationOptions'
);
$Instance = $this->getMock('SearchListener', $mocked, array($CrudSubject));
$Instance->scope('sample', array('test' => 1));
$Instance
->expects($this->once())
->method('_checkRequiredPlugin');
$Instance
->expects($this->once())
->method('_ensureComponent')
->with($Controller);
$Instance
->expects($this->once())
->method('_ensureBehavior')
->with($Model);
$Instance
->expects($this->once())
->method('_commonProcess')
->with($Controller, 'Model');
$Instance
->expects($this->never())
->method('_setFilterArgs', 'Should not be called when model got filterArgs already');
$Instance
->expects($this->once())
->method('_setPaginationOptions')
->with($Controller, $Model, array('test' => 1));
$Instance->beforePaginate(new CakeEvent('beforePaginate', $CrudSubject));
}
/**
* Test beforePaginate
*
* Test that query scope works with a defined
* query scope in the listener and a filter
*
* @covers SearchListener::beforePaginate
* @return void
*/
public function testBeforePaginateWithDefinedQueryScopeAndFilter() {
$Model = new Model();
$Model->filterArgs = array('sample' => 'test');
$Request = new CakeRequest();
$Request->query['_scope'] = 'sample';
$Controller = new Controller();
$CrudSubject = new CrudSubject(array(
'request' => $Request,
'controller' => $Controller,
'model' => $Model
));
$mocked = array(
'_checkRequiredPlugin',
'_ensureComponent',
'_ensureBehavior',
'_commonProcess',
'_setFilterArgs',
'_setPaginationOptions'
);
$Instance = $this->getMock('SearchListener', $mocked, array($CrudSubject));
$Instance->scope('sample', array('test' => 1), array('filter' => true));
$Instance
->expects($this->once())
->method('_checkRequiredPlugin');
$Instance
->expects($this->once())
->method('_ensureComponent')
->with($Controller);
$Instance
->expects($this->once())
->method('_ensureBehavior')
->with($Model);
$Instance
->expects($this->once())
->method('_commonProcess')
->with($Controller, 'Model');
$Instance
->expects($this->once())
->method('_setFilterArgs')
->with($Model, array('filter' => true));
$Instance
->expects($this->once())
->method('_setPaginationOptions')
->with($Controller, $Model, array('test' => 1));
$Instance->beforePaginate(new CakeEvent('beforePaginate', $CrudSubject));
}
/**
* Test that _checkRequiredPlugin doesn't throw an exception
*
* @covers SearchListener::_checkRequiredPlugin
* @return void
*/
public function testCheckRequiredPlugins() {
$Action = $this->getMock('stdClass', array('config'));
$Crud = $this->getMock('stdClass', array('action'));
$Crud
->expects($this->once())
->method('action')
->will($this->returnValue($Action));
$Model = new Model();
$Model->filterArgs = array('sample' => 'test');
$Request = new CakeRequest();
$Controller = new Controller();
$CrudSubject = new CrudSubject(array(
'request' => $Request,
'crud' => $Crud,
'controller' => $Controller,
'model' => $Model
));
$mocked = array(
'_ensureComponent',
'_ensureBehavior',
'_commonProcess',
'_setFilterArgs',
'_setPaginationOptions'
);
$Instance = $this->getMock('SearchListener', $mocked, array($CrudSubject));
$Instance
->expects($this->once())
->method('_ensureComponent')
->with($Controller);
$Instance
->expects($this->once())
->method('_ensureBehavior')
->with($Model);
$Instance
->expects($this->once())
->method('_commonProcess')
->with($Controller, 'Model');
$Instance
->expects($this->never())
->method('_setFilterArgs');
$Instance
->expects($this->once())
->method('_setPaginationOptions')
->with($Controller, $Model, array());
$Instance->beforePaginate(new CakeEvent('beforePaginate', $CrudSubject));
}
/**
* Test that _checkRequiredPlugin doesn't throw an exception
*
* @covers SearchListener::_checkRequiredPlugin
* @return void
*/
public function testCheckRequiredPluginsWithoutPlugin() {
CakePlugin::unload('Search');
$this->setExpectedException(
'CakeException',
'SearchListener requires the CakeDC/search plugin. Please install it from https://github.com/CakeDC/search'
);
$Model = new Model();
$Model->filterArgs = array('sample' => 'test');
$Request = new CakeRequest();
$Controller = new Controller();
$CrudSubject = new CrudSubject(array(
'request' => $Request,
'controller' => $Controller,
'model' => $Model
));
$mocked = array(
'_ensureComponent',
'_ensureBehavior',
'_commonProcess',
'_setFilterArgs',
'_setPaginationOptions'
);
$Instance = $this->getMock('SearchListener', $mocked, array($CrudSubject));
$Instance
->expects($this->never())
->method('_ensureComponent');
$Instance
->expects($this->never())
->method('_ensureBehavior');
$Instance
->expects($this->never())
->method('_commonProcess');
$Instance
->expects($this->never())
->method('_setFilterArgs');
$Instance
->expects($this->never())
->method('_setPaginationOptions');
$Instance->beforePaginate(new CakeEvent('beforePaginate', $CrudSubject));
}
/**
* Test that the Prg component is automatically initialized
* if its not loaded by the controller directly
*
* @covers SearchListener::_ensureComponent
* @return void
*/
public function testEnsureComponent() {
$Controller = new Controller(new CakeRequest());
$Component = $this->getMock('Component', array('initialize', 'startup'), array(), '', false);
$Component
->expects($this->once())
->method('initialize')
->with($Controller);
$Component
->expects($this->once())
->method('startup')
->with($Controller);
$Controller->Components = $this->getMock('ComponentCollection', array('loaded', 'load'));
$Controller->Components
->expects($this->once())
->method('loaded')
->with('Prg')
->will($this->returnValue(false));
$Controller->Components
->expects($this->once())
->method('load')
->with('Search.Prg')
->will($this->returnValue($Component));
$Instance = new SearchListener(new CrudSubject());
$Method = new ReflectionMethod('SearchListener', '_ensureComponent');
$Method->setAccessible(true);
$Method->invoke($Instance, $Controller);
}
/**
* Test that nothing is done if the Prg component is already loaded
*
* @covers SearchListener::_ensureComponent
* @return void
*/
public function testEnsureComponentAlreadyLoaded() {
$Controller = new Controller(new CakeRequest());
$Controller->Components = $this->getMock('ComponentCollection', array('loaded', 'load'));
$Controller->Components
->expects($this->once())
->method('loaded')
->with('Prg')
->will($this->returnValue(true));
$Controller->Components
->expects($this->never())
->method('load');
$Instance = new SearchListener(new CrudSubject());
$Method = new ReflectionMethod('SearchListener', '_ensureComponent');
$Method->setAccessible(true);
$Method->invoke($Instance, $Controller);
}
/**
* Test that the Searchable behavior is automatically initialized
* if its not loaded by the model directly
*
* @covers SearchListener::_ensureBehavior
* @return void
*/
public function testEnsureBehavior() {
$Model = new Model();
$Behavior = $this->getMock('Behavior', array('setup'), array(), '', false);
$Behavior
->expects($this->once())
->method('setup')
->with($Model);
$Model->Behaviors = $this->getMock('BehaviorCollection', array('loaded', 'load'));
$Model->Behaviors->Searchable = $Behavior;
$Model->Behaviors
->expects($this->once())
->method('loaded')
->with('Searchable')
->will($this->returnValue(false));
$Model->Behaviors
->expects($this->once())
->method('load')
->with('Search.Searchable');
$Instance = new SearchListener(new CrudSubject());
$Method = new ReflectionMethod('SearchListener', '_ensureBehavior');
$Method->setAccessible(true);
$Method->invoke($Instance, $Model);
}
/**
* Test that nothing is done if the Searchable behavior is already loaded
*
* @covers SearchListener::_ensureBehavior
* @return void
*/
public function testEnsureBehaviorAlreadyLoaded() {
$Model = new Model();
$Behavior = $this->getMock('Behavior', array('setup'), array(), '', false);
$Behavior
->expects($this->never())
->method('setup');
$Model->Behaviors = $this->getMock('BehaviorCollection', array('loaded', 'load'));
$Model->Behaviors->Searchable = $Behavior;
$Model->Behaviors
->expects($this->once())
->method('loaded')
->with('Searchable')
->will($this->returnValue(true));
$Model->Behaviors
->expects($this->never())
->method('load');
$Instance = new SearchListener(new CrudSubject());
$Method = new ReflectionMethod('SearchListener', '_ensureBehavior');
$Method->setAccessible(true);
$Method->invoke($Instance, $Model);
}
public function test_setPaginationOptions() {
$Controller = new Controller();
$Controller->Paginator = new StdClass();
$Controller->Paginator->settings = array();
$Model = $this->getMock('Model', array('parseCriteria'));
$Model
->expects($this->once())
->method('parseCriteria')
->will($this->returnValue(array('some' => 'conditions')));
$Instance = new SearchListener(new CrudSubject());
$Method = new ReflectionMethod('SearchListener', '_setPaginationOptions');
$Method->setAccessible(true);
$Method->invoke($Instance, $Controller, $Model, array());
$expected = array(
'some' => 'conditions'
);
$result = $Controller->Paginator->settings['conditions'];
$this->assertSame($expected, $result, 'Conditions should match what the model says');
}
public function test_setPaginationOptionsMerge() {
$Controller = new Controller();
$Controller->Paginator = new StdClass();
$Controller->Paginator->settings = array(
'conditions' => array(
'existing' => 'conditions'
)
);
$Model = $this->getMock('Model', array('parseCriteria'));
$Model
->expects($this->once())
->method('parseCriteria')
->will($this->returnValue(array('some' => 'conditions')));
$Instance = new SearchListener(new CrudSubject());
$Method = new ReflectionMethod('SearchListener', '_setPaginationOptions');
$Method->setAccessible(true);
$Method->invoke($Instance, $Controller, $Model, array());
$expected = array(
'existing' => 'conditions',
'some' => 'conditions'
);
$result = $Controller->Paginator->settings['conditions'];
$this->assertSame($expected, $result, 'Existing conditions should not be removed');
}
public function test_setPaginationOptionsClobber() {
$Controller = new Controller();
$Controller->Paginator = new StdClass();
$Controller->Paginator->settings = array(
'conditions' => array(
'some' => 'other conditions'
)
);
$Model = $this->getMock('Model', array('parseCriteria'));
$Model
->expects($this->once())
->method('parseCriteria')
->will($this->returnValue(array('some' => 'conditions')));
$Instance = new SearchListener(new CrudSubject());
$Method = new ReflectionMethod('SearchListener', '_setPaginationOptions');
$Method->setAccessible(true);
$Method->invoke($Instance, $Controller, $Model, array());
$expected = array(
'some' => 'conditions'
);
$result = $Controller->Paginator->settings['conditions'];
$this->assertSame($expected, $result, 'Existing conditions should be overwritten');
}
}

View File

@ -1,528 +0,0 @@
<?php
App::uses('Controller', 'Controller');
App::uses('CakeRequest', 'Network');
App::uses('CakeResponse', 'Network');
App::uses('CrudExceptionRenderer', 'Crud.Error');
App::uses('CrudValidationException', 'Crud.Error/Exception');
App::uses('ConnectionManager', 'Model');
class CrudExceptionRendererTest extends CakeTestCase {
public $fixtures = array('core.post');
public function testNormalExceptionRendering() {
Configure::write('debug', 1);
$Exception = new CakeException('Hello World');
$Controller = $this->getMock('Controller', array('render'));
$Controller->request = new CakeRequest();
$Controller->response = new CakeResponse();
$Renderer = $this->getMock('CrudExceptionRenderer', array('_getController'), array(), '', false);
$Renderer
->expects($this->once())
->method('_getController')
->with($Exception)
->will($this->returnValue($Controller));
$Renderer->__construct($Exception);
$Renderer->render();
$viewVars = $Controller->viewVars;
$this->assertTrue(!empty($viewVars['_serialize']));
$expected = array('success', 'data');
$actual = $viewVars['_serialize'];
$this->assertEquals($expected, $actual);
$expected = array(
'code' => 500,
'url' => $Controller->request->here(),
'name' => 'Hello World',
'exception' => array(
'class' => 'CakeException',
'code' => 500,
'message' => 'Hello World',
)
);
$actual = $viewVars['data'];
unset($actual['exception']['trace']);
$this->assertEquals($expected, $actual);
$this->assertTrue(!isset($actual['queryLog']));
$this->assertTrue(isset($viewVars['success']));
$this->assertFalse($viewVars['success']);
$this->assertTrue(isset($viewVars['code']));
$this->assertSame(500, $viewVars['code']);
$this->assertTrue(isset($viewVars['url']));
$this->assertSame($Controller->request->here(), $viewVars['url']);
$this->assertTrue(isset($viewVars['name']));
$this->assertSame('Hello World', $viewVars['name']);
$this->assertTrue(isset($viewVars['error']));
$this->assertSame($Exception, $viewVars['error']);
}
public function testNormalExceptionRenderingQueryLog() {
Configure::write('debug', 2);
$Exception = new CakeException('Hello World');
$Controller = $this->getMock('Controller', array('render'));
$Controller->request = new CakeRequest();
$Controller->response = new CakeResponse();
$Renderer = $this->getMock('CrudExceptionRenderer', array('_getController'), array(), '', false);
$Renderer
->expects($this->once())
->method('_getController')
->with($Exception)
->will($this->returnValue($Controller));
$Renderer->__construct($Exception);
$Renderer->render();
$viewVars = $Controller->viewVars;
$this->assertTrue(!empty($viewVars['_serialize']));
$expected = array('success', 'data');
$actual = $viewVars['_serialize'];
$this->assertEquals($expected, $actual);
$expected = array(
'code' => 500,
'url' => $Controller->request->here(),
'name' => 'Hello World',
'exception' => array(
'class' => 'CakeException',
'code' => 500,
'message' => 'Hello World',
)
);
$actual = $viewVars['data'];
$queryLog = $actual['queryLog'];
unset($actual['exception']['trace']);
unset($actual['queryLog']);
$this->assertEquals($expected, $actual);
$this->assertTrue(!empty($queryLog));
$this->assertTrue(isset($queryLog['test']));
$this->assertTrue(isset($queryLog['test']['log']));
$this->assertTrue(isset($queryLog['test']['count']));
$this->assertTrue(isset($queryLog['test']['time']));
$this->assertTrue(isset($viewVars['success']));
$this->assertFalse($viewVars['success']);
$this->assertTrue(isset($viewVars['code']));
$this->assertSame(500, $viewVars['code']);
$this->assertTrue(isset($viewVars['url']));
$this->assertSame($Controller->request->here(), $viewVars['url']);
$this->assertTrue(isset($viewVars['name']));
$this->assertSame('Hello World', $viewVars['name']);
$this->assertTrue(isset($viewVars['error']));
$this->assertSame($Exception, $viewVars['error']);
}
public function testNormalNestedExceptionRendering() {
Configure::write('debug', 1);
$Exception = new CakeException('Hello World');
$Controller = $this->getMock('Controller', array('render'));
$Controller->request = new CakeRequest();
$Controller->response = new CakeResponse();
$Renderer = $this->getMock('CrudExceptionRenderer', array('_getController'), array(), '', false);
$Renderer
->expects($this->once())
->method('_getController')
->with($Exception)
->will($this->returnValue($Controller));
$Renderer->__construct($Exception);
$Renderer->render();
$viewVars = $Controller->viewVars;
$this->assertTrue(!empty($viewVars['_serialize']));
$expected = array('success', 'data');
$actual = $viewVars['_serialize'];
$this->assertEquals($expected, $actual);
$expected = array(
'code' => 500,
'url' => $Controller->request->here(),
'name' => 'Hello World',
'exception' => array(
'class' => 'CakeException',
'code' => 500,
'message' => 'Hello World',
)
);
$actual = $viewVars['data'];
unset($actual['exception']['trace']);
$this->assertEquals($expected, $actual);
$this->assertTrue(isset($viewVars['success']));
$this->assertFalse($viewVars['success']);
$this->assertTrue(isset($viewVars['code']));
$this->assertSame(500, $viewVars['code']);
$this->assertTrue(isset($viewVars['url']));
$this->assertSame($Controller->request->here(), $viewVars['url']);
$this->assertTrue(isset($viewVars['name']));
$this->assertSame('Hello World', $viewVars['name']);
$this->assertTrue(isset($viewVars['error']));
$this->assertSame($Exception, $viewVars['error']);
}
public function testMissingViewExceptionDuringRendering() {
Configure::write('debug', 1);
$Exception = new CakeException('Hello World');
$Controller = $this->getMock('Controller', array('render'));
$Controller->request = new CakeRequest();
$Controller->response = $this->getMock('CakeResponse', array('send'));
$Controller->response
->expects($this->at(0))
->method('send')
->will($this->throwException(new MissingViewException('boo')));
$Renderer = $this->getMock('CrudExceptionRenderer', array('_getController'), array(), '', false);
$Renderer
->expects($this->once())
->method('_getController')
->with($Exception)
->will($this->returnValue($Controller));
$Renderer->__construct($Exception);
$Renderer->render();
$viewVars = $Controller->viewVars;
$this->assertTrue(!empty($viewVars['_serialize']));
$expected = array('success', 'data');
$actual = $viewVars['_serialize'];
$this->assertEquals($expected, $actual);
$expected = array(
'code' => 500,
'url' => $Controller->request->here(),
'name' => 'Hello World',
'exception' => array(
'class' => 'CakeException',
'code' => 500,
'message' => 'Hello World',
)
);
$actual = $viewVars['data'];
unset($actual['exception']['trace']);
$this->assertEquals($expected, $actual);
$this->assertTrue(isset($viewVars['success']));
$this->assertFalse($viewVars['success']);
$this->assertTrue(isset($viewVars['code']));
$this->assertSame(500, $viewVars['code']);
$this->assertTrue(isset($viewVars['url']));
$this->assertSame($Controller->request->here(), $viewVars['url']);
$this->assertTrue(isset($viewVars['name']));
$this->assertSame('Hello World', $viewVars['name']);
$this->assertTrue(isset($viewVars['error']));
$this->assertSame($Exception, $viewVars['error']);
}
public function testGenericExceptionDuringRendering() {
Configure::write('debug', 1);
$Exception = new CakeException('Hello World');
$NestedException = new CakeException('Generic Exception Description');
$Controller = $this->getMock('Controller', array('render'));
$Controller->request = new CakeRequest();
$Controller->response = $this->getMock('CakeResponse', array('send'));
$Controller->response
->expects($this->at(0))
->method('send')
->will($this->throwException($NestedException));
$Renderer = $this->getMock('CrudExceptionRenderer', array('_getController'), array(), '', false);
$Renderer
->expects($this->once())
->method('_getController')
->with($Exception)
->will($this->returnValue($Controller));
$Renderer->__construct($Exception);
$Renderer->render();
$viewVars = $Controller->viewVars;
$this->assertTrue(!empty($viewVars['_serialize']));
$expected = array('success', 'data');
$actual = $viewVars['_serialize'];
$this->assertEquals($expected, $actual);
$expected = array(
'code' => 500,
'url' => $Controller->request->here(),
'name' => 'Hello World',
'exception' => array(
'class' => 'CakeException',
'code' => 500,
'message' => 'Hello World',
)
);
$actual = $viewVars['data'];
unset($actual['exception']['trace']);
$this->assertEquals($expected, $actual);
$this->assertTrue(isset($viewVars['success']));
$this->assertFalse($viewVars['success']);
$this->assertTrue(isset($viewVars['code']));
$this->assertSame(500, $viewVars['code']);
$this->assertTrue(isset($viewVars['url']));
$this->assertSame($Controller->request->here(), $viewVars['url']);
$this->assertTrue(isset($viewVars['name']));
$this->assertSame('Generic Exception Description', $viewVars['name']);
$this->assertTrue(isset($viewVars['error']));
$this->assertSame($NestedException, $viewVars['error']);
}
public function testValidationErrorSingleKnownError() {
$Model = ClassRegistry::init(array('class' => 'Model', 'alias' => 'Alias', 'table' => false));
$Model->validate = array(
'field' => array(
array(
'rule' => 'custom',
'message' => 'boom'
)
)
);
$Model->invalidate('field', 'boom');
$Exception = new CrudValidationException(array(
'Alias' => array(
'field' => array(
'boom'
)
)
));
$Controller = $this->getMock('Controller', array('render'));
$Controller->request = new CakeRequest();
$Controller->response = new CakeResponse();
$Renderer = $this->getMock('CrudExceptionRenderer', array('_getController'), array(), '', false);
$Renderer
->expects($this->once())
->method('_getController')
->with($Exception)
->will($this->returnValue($Controller));
$Renderer->__construct($Exception);
Configure::write('debug', 0);
$Renderer->render();
Configure::write('debug', 1);
$expected = array(
'code' => 412,
'url' => $Controller->request->here(),
'name' => 'Alias.field : boom',
'errorCount' => 1,
'errors' => array(
'Alias' => array(
'field' => array(
'boom'
)
)
),
'exception' => array(
'class' => 'CrudValidationException',
'code' => 412,
'message' => 'Alias.field : boom'
)
);
$this->assertEquals($expected, $Controller->viewVars['data']);
}
public function testValidationErrorSingleKnownErrorWithCode() {
$Model = ClassRegistry::init(array('class' => 'Model', 'alias' => 'Alias', 'table' => false));
$Model->validate = array(
'field' => array(
array(
'rule' => 'custom',
'message' => 'boom',
'code' => 1000
)
)
);
$Model->invalidate('field', 'boom');
$Exception = new CrudValidationException(array(
'Alias' => array(
'field' => array(
'boom'
)
)
));
$Controller = $this->getMock('Controller', array('render'));
$Controller->request = new CakeRequest();
$Controller->response = new CakeResponse();
$Renderer = $this->getMock('CrudExceptionRenderer', array('_getController'), array(), '', false);
$Renderer
->expects($this->once())
->method('_getController')
->with($Exception)
->will($this->returnValue($Controller));
$Renderer->__construct($Exception);
Configure::write('debug', 0);
$Renderer->render();
Configure::write('debug', 1);
$expected = array(
'code' => 1000,
'url' => $Controller->request->here(),
'name' => 'Alias.field : boom',
'errorCount' => 1,
'errors' => array(
'Alias' => array(
'field' => array(
'boom'
)
)
),
'exception' => array(
'class' => 'CrudValidationException',
'code' => 1000,
'message' => 'Alias.field : boom'
)
);
$this->assertEquals($expected, $Controller->viewVars['data']);
}
public function testValidationErrorMultipleMessages() {
$Exception = new CrudValidationException(array(
'Alias' => array(
'field' => array(
'something wrong with this field'
),
'another_field' => array(
'something wrong with this field'
)
)
));
$Controller = $this->getMock('Controller', array('render'));
$Controller->request = new CakeRequest();
$Controller->response = new CakeResponse();
$Renderer = $this->getMock('CrudExceptionRenderer', array('_getController'), array(), '', false);
$Renderer
->expects($this->once())
->method('_getController')
->with($Exception)
->will($this->returnValue($Controller));
$Renderer->__construct($Exception);
Configure::write('debug', 0);
$Renderer->render();
Configure::write('debug', 1);
$expected = array(
'code' => 412,
'url' => $Controller->request->here(),
'name' => '2 validation errors occurred',
'errorCount' => 2,
'errors' => array(
'Alias' => array(
'field' => array(
'something wrong with this field'
),
'another_field' => array(
'something wrong with this field'
)
)
),
'exception' => array(
'class' => 'CrudValidationException',
'code' => 412,
'message' => '2 validation errors occurred',
)
);
$this->assertEquals($expected, $Controller->viewVars['data']);
}
public function testValidationErrorUnknownModel() {
$Exception = new CrudValidationException(array(
'Alias' => array(
'field' => array(
'something wrong with this field'
)
)
));
$Controller = $this->getMock('Controller', array('render'));
$Controller->request = new CakeRequest();
$Controller->response = new CakeResponse();
$Renderer = $this->getMock('CrudExceptionRenderer', array('_getController'), array(), '', false);
$Renderer
->expects($this->once())
->method('_getController')
->with($Exception)
->will($this->returnValue($Controller));
$Renderer->__construct($Exception);
Configure::write('debug', 0);
$Renderer->render();
Configure::write('debug', 1);
$expected = array(
'code' => 412,
'url' => $Controller->request->here(),
'name' => 'A validation error occurred',
'errorCount' => 1,
'errors' => array(
'Alias' => array(
'field' => array(
'something wrong with this field'
)
)
),
'exception' => array(
'class' => 'CrudValidationException',
'code' => 412,
'message' => 'A validation error occurred',
)
);
$this->assertEquals($expected, $Controller->viewVars['data']);
}
}

View File

@ -1,89 +0,0 @@
<?php
App::uses('Controller', 'Controller');
App::uses('CrudPanel', 'Crud.Panel');
App::uses('CrudTestCase', 'Crud.Test/Support');
class CrudPanelTest extends CrudTestCase {
/**
* setUp
*
* @return void
*/
public function setUp() {
parent::setUp();
if (!CakePlugin::loaded('DebugKit')) {
try {
CakePlugin::load('DebugKit');
} catch (MissingPluginException $e) {
$this->markTestSkipped('DebugKit plugin not available');
}
}
$this->Controller = new Controller();
$this->Panel = new CrudPanel();
$this->setReflectionClassInstance($this->Panel);
}
public function testGetCallbacks() {
$listeners = array(
array(
'callable' => array(
'SomeClass',
'someStaticMethod'
)
),
array(
'callable' => array(
$this,
'someInstanceMethod'
),
),
array(
'callable' => function() {
return 'Some Closure';
}
)
);
$line = __LINE__;
$path = Debugger::trimPath(__FILE__);
$expected = array(
'SomeClass::someStaticMethod',
'CrudPanelTest::someInstanceMethod',
$path . ':' . ($line - 5)
);
$return = $this->callProtectedMethod('_getCallbacks', array($listeners), $this->Panel);
$this->assertSame($expected, $return);
}
/**
* test_getUniqueName
*
* @return void
*/
public function testGetUniqueName() {
$name = 'name';
$existing = array();
$return = $this->callProtectedMethod('_getUniqueName', array($name, $existing), $this->Panel);
$this->assertSame('name', $return, 'A unique name should not be modified');
}
public function testGetUniqueNameCollision() {
$name = 'name';
$existing = array('name' => array());
$return = $this->callProtectedMethod('_getUniqueName', array($name, $existing), $this->Panel);
$this->assertSame('name #2', $return, 'A collision should cause a suffix to be added');
}
public function testGetUniqueNameMultipleCollision() {
$name = 'name';
$existing = array('name' => array(), 'name #2' => array(), 'name #3' => array());
$return = $this->callProtectedMethod('_getUniqueName', array($name, $existing), $this->Panel);
$this->assertSame('name #4', $return, 'The suffix should always be one more than any existing defined names');
}
}

View File

@ -1,51 +0,0 @@
<?php
App::uses('FakeHeadFilter', 'Crud.Routing/Filter');
App::uses('CakeRequest', 'Network');
App::uses('CakeResponse', 'Network');
/**
* FakeHeadFilterTest
*
*/
class FakeHeadFilterTest extends CakeTestCase {
/**
* testNoop
*
* @return void
*/
public function testNoop() {
$filter = new FakeHeadFilter();
$response = $this->getMock('CakeResponse', array('_sendHeader'));
$request = new CakeRequest('/');
$event = new CakeEvent('FakeHeadFilterTest', $this, compact('request', 'response'));
$_SERVER['REQUEST_METHOD'] = 'ORIGINAL';
$this->assertNull($filter->beforeDispatch($event), 'No action should be taken, nothing returned');
$this->assertSame('ORIGINAL', $_SERVER['REQUEST_METHOD'], 'Request method should be unmodified');
$this->assertNull($filter->afterDispatch($event), 'No action should be taken, nothing returned');
$this->assertSame('ORIGINAL', $_SERVER['REQUEST_METHOD'], 'Request method should be unmodified');
}
/**
* testHead
*
* Simulate a get request, return a head response
*
* @return void
*/
public function testHead() {
$filter = new FakeHeadFilter();
$response = $this->getMock('CakeResponse', array('_sendHeader'));
$request = new CakeRequest('/');
$event = new CakeEvent('FakeHeadFilterTest', $this, compact('request', 'response'));
$_SERVER['REQUEST_METHOD'] = 'HEAD';
$this->assertNull($filter->beforeDispatch($event), 'No action should be taken, nothing returned');
$this->assertSame('GET', $_SERVER['REQUEST_METHOD'], 'Request method should now be GET');
$this->assertNull($filter->afterDispatch($event), 'No action should be taken, nothing returned');
$this->assertSame('HEAD', $_SERVER['REQUEST_METHOD'], 'Request method should now be back to HEAD');
}
}

View File

@ -1,207 +0,0 @@
<?php
App::uses('HttpMethodFilter', 'Crud.Routing/Filter');
App::uses('CakeRequest', 'Network');
App::uses('CakeResponse', 'Network');
/**
* HttpMethodFilterTest
*
*/
class HttpMethodFilterTest extends CakeTestCase {
/**
* testNoop
*
* @return void
*/
public function testNoop() {
Router::reload();
Router::connect('/:controller/:action/*');
$filter = new HttpMethodFilter();
$response = $this->getMock('CakeResponse', array('_sendHeader'));
$request = new CakeRequest('controller/action/1');
$event = new CakeEvent('HttpMethodFilterTest', $this, compact('request', 'response'));
$this->assertNull($filter->beforeDispatch($event), 'The HttpMethod filter should return null if it does nothing');
$this->assertFalse($event->isStopped(), 'The HttpMethod filter should not stop the event for !OPTIONS requests');
$this->assertNull($filter->afterDispatch($event), 'The HttpMethod filter should return null if it does nothing');
}
/**
* testOptions
*
* @return void
*/
public function testOptions() {
Router::reload();
Router::connect('/:controller/:action/*');
$filter = new HttpMethodFilter();
$response = $this->getMock('CakeResponse', array('_sendHeader'));
$request = new CakeRequest('controller/action/1');
$request->addDetector('options', array(
'callback' => function() {
return true;
}
));
$event = new CakeEvent('HttpMethodFilterTest', $this, compact('request', 'response'));
$this->assertSame($response, $filter->beforeDispatch($event), 'The HttpMethod filter should return a response');
$this->assertTrue($event->isStopped(), 'The HttpMethod filter should stop the event');
$expected = array(
'Access-Control-Allow-Methods' => 'GET, HEAD, POST, PUT, DELETE'
);
$this->assertSame($expected, $response->header(), 'A standard route accepts all verbs');
}
/**
* testOptionsRestrictedVerbs
*
* @return void
*/
public function testOptionsRestrictedVerbs() {
Router::reload();
Router::connect('/:controller/:action/*', array('[method]' => 'GET'));
$filter = new HttpMethodFilter();
$response = $this->getMock('CakeResponse', array('_sendHeader'));
$request = new CakeRequest('controller/action/1');
$request->addDetector('options', array(
'callback' => function() {
return true;
}
));
$event = new CakeEvent('HttpMethodFilterTest', $this, compact('request', 'response'));
$this->assertSame($response, $filter->beforeDispatch($event), 'The HttpMethod filter should return a response');
$this->assertTrue($event->isStopped(), 'The HttpMethod filter should stop the event');
$expected = array(
'Access-Control-Allow-Methods' => 'GET'
);
$this->assertSame($expected, $response->header(), 'Only verbs for matching routes should be returned');
}
/**
* testOptionsCustomVerbs
*
* @return void
*/
public function testOptionsCustomVerbs() {
Router::reload();
Router::connect('/:controller/:action/*', array('[method]' => 'TICKLE'));
Router::connect('/:controller/:action/*', array('[method]' => 'ANNOY'));
Configure::write('Crud.HttpMethodFilter.verbs', array('GET', 'TICKLE', 'ANNOY'));
$filter = new HttpMethodFilter();
$response = $this->getMock('CakeResponse', array('_sendHeader'));
$request = new CakeRequest('controller/action/1');
$request->addDetector('options', array(
'callback' => function() {
return true;
}
));
$event = new CakeEvent('HttpMethodFilterTest', $this, compact('request', 'response'));
$this->assertSame($response, $filter->beforeDispatch($event), 'The HttpMethod filter should return a response');
$this->assertTrue($event->isStopped(), 'The HttpMethod filter should stop the event');
$expected = array(
'Access-Control-Allow-Methods' => 'TICKLE, ANNOY'
);
$this->assertSame($expected, $response->header(), 'A verbs for matching routes should be returned');
}
/**
* testHead
*
* Simulate a get request, return a head response
*
* @return void
*/
public function testHead() {
$filter = new HttpMethodFilter();
$response = $this->getMock('CakeResponse', array('_sendHeader'));
$response->body('some content');
$request = new CakeRequest('controller/action/1');
$request->addDetector('head', array(
'callback' => function() {
return true;
}
));
$event = new CakeEvent('HttpMethodFilterTest', $this, compact('request', 'response'));
$this->assertSame($response, $filter->afterDispatch($event), 'The HttpMethod filter should return a response');
$expected = array(
'Content-length' => '12'
);
$this->assertSame($expected, $response->header(), 'The content header should be set');
$this->assertSame('', $response->body(), 'The body should be removed');
}
/**
* testHeadEmpty
*
* If there's no body, don't assume a GET request for it would be empty
*
* @return void
*/
public function testHeadEmpty() {
$filter = new HttpMethodFilter();
$response = $this->getMock('CakeResponse', array('_sendHeader'));
$request = new CakeRequest('controller/action/1');
$request->addDetector('head', array(
'callback' => function() {
return true;
}
));
$event = new CakeEvent('HttpMethodFilterTest', $this, compact('request', 'response'));
$this->assertSame($response, $filter->afterDispatch($event), 'The HttpMethod filter should return a response');
$expected = array();
$this->assertSame($expected, $response->header(), 'There is no body, the content-length header should be empty');
$this->assertSame('', $response->body(), 'The body should be removed');
}
/**
* testHeadHandled
*
* Simulate app code having handled the head request appropriately
*
* @return void
*/
public function testHeadHandled() {
$filter = new HttpMethodFilter();
$response = $this->getMock('CakeResponse', array('_sendHeader'));
$response->header('Content-length', 123);
$request = new CakeRequest('controller/action/1');
$request->addDetector('head', array(
'callback' => function() {
return true;
}
));
$event = new CakeEvent('HttpMethodFilterTest', $this, compact('request', 'response'));
$this->assertSame($response, $filter->afterDispatch($event), 'The HttpMethod filter should return a response');
$expected = array(
'Content-length' => '123'
);
$this->assertSame($expected, $response->header(), 'The content header should be set');
$this->assertSame('', $response->body(), 'The body should remain empty');
}
}

View File

@ -1,35 +0,0 @@
<?php
/**
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
class PostsTagFixture extends CakeTestFixture {
/**
* Table to be created
*
* @var string 'posts_tags'
*/
public $table = 'posts_tags';
/**
* fields property
*
* @var array
*/
public $fields = array(
'id' => array('type' => 'integer', 'key' => 'primary'),
'post_id' => array('type' => 'integer', 'null' => false),
'tag_id' => array('type' => 'integer', 'null' => false)
);
/**
* records property
*
* @var array
*/
public $records = array(
);
}

View File

@ -1,169 +0,0 @@
<?php
/**
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
abstract class CrudControllerTestCase extends ControllerTestCase {
/**
* List of Reflection properties made public
*
* @var array
*/
protected $_reflectionPropertyCache = array();
/**
* List of Reflection methods made public
*
* @var array
*/
protected $_reflectionMethodCache = array();
/**
* List of class name <=> instance used for invocation
*
* @var array
*/
protected $_reflectionInstanceCache = array();
public function setUp() {
parent::setUp();
$this->resetReflectionCache();
}
/**
* Reset the internal reflection caches
*
* @return void
*/
public function resetReflectionCache() {
$this->_reflectionPropertyCache = array();
$this->_reflectionMethodCache = array();
$this->_reflectionInstanceCache = array();
}
/**
* Map a instance of a object to its class name
*
* @param Object $instance
* @return void
*/
public function setReflectionClassInstance($instance, $class = null) {
$class = $class ?: get_class($instance);
$this->_reflectionInstanceCache[$class] = $instance;
}
/**
* Get working instance of "$class"
*
* @param string $class
* @return Object
* @throws Exception When the reflection instance cannot be found
*/
public function getReflectionInstance($class) {
$class = $this->_getReflectionTargetClass($class);
if (empty($this->_reflectionInstanceCache[$class])) {
throw new Exception(sprintf('Unable to find instance of %s in the reflection cache. Have you added it using "setReflectionClassInstance"?', $class));
}
return $this->_reflectionInstanceCache[$class];
}
/**
* Helper method to call a protected method
*
* @param string $method
* @param array $args Argument list to call $method with (call_user_func_array style)
* @param string $class Target reflection class
* @return mixed
*/
public function callProtectedMethod($method, $args = array(), $class = null) {
$class = $this->_getReflectionTargetClass($class);
$cacheKey = $class . '_' . $method;
if (!in_array($cacheKey, $this->_reflectionMethodCache)) {
$this->_reflectionMethodCache[$cacheKey] = new ReflectionMethod($class, $method);
$this->_reflectionMethodCache[$cacheKey]->setAccessible(true);
}
return $this->_reflectionMethodCache[$cacheKey]->invokeArgs($this->getReflectionInstance($class), $args);
}
/**
* Helper method to get the value of a protected property
*
* @param string $property
* @param string $class Target reflection class
* @return mixed
*/
public function getProtectedProperty($property, $class = null) {
$Instance = $this->_getReflectionPropertyInstance($property, $class);
return $Instance->getValue($this->getReflectionInstance($class));
}
/**
* Helper method to set the value of a protected property
*
* @param string $property
* @param mixed $value
* @param string $class Target reflection class
* @return mixed
*/
public function setProtectedProperty($property, $value, $class = null) {
$Instance = $this->_getReflectionPropertyInstance($property, $class);
return $Instance->setValue($this->getReflectionInstance($class), $value);
}
/**
* Get a reflection property object
*
* @param string $property
* @param string $class
* @return ReflectionProperty
*/
protected function _getReflectionPropertyInstance($property, $class) {
$class = $this->_getReflectionTargetClass($class);
$cacheKey = $class . '_' . $property;
if (!in_array($cacheKey, $this->_reflectionPropertyCache)) {
$this->_reflectionPropertyCache[$cacheKey] = new ReflectionProperty($class, $property);
$this->_reflectionPropertyCache[$cacheKey]->setAccessible(true);
}
return $this->_reflectionPropertyCache[$cacheKey];
}
/**
* Get the reflection class name
*
* @param string $class
* @return string
* @throws Exception When the reflection target cannot be found
*
*/
protected function _getReflectionTargetClass($class) {
if (is_object($class)) {
$class = get_class($class);
}
if (!empty($class)) {
return $class;
}
if (isset($this->defaultRelfectionTarget)) {
$class = $this->defaultRelfectionTarget;
if (is_object($class)) {
$class = get_class($class);
}
}
if (empty($class)) {
throw new Exception(sprintf('Unable to find reflection target; have you set $defaultRelfectionTarget or passed in class name?', $class));
}
return $class;
}
}

View File

@ -1,168 +0,0 @@
<?php
/**
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*/
abstract class CrudTestCase extends CakeTestCase {
/**
* List of Reflection properties made public
*
* @var array
*/
protected $_reflectionPropertyCache = array();
/**
* List of Reflection methods made public
*
* @var array
*/
protected $_reflectionMethodCache = array();
/**
* List of class name <=> instance used for invocation
*
* @var array
*/
protected $_reflectionInstanceCache = array();
public function setUp() {
parent::setUp();
$this->resetReflectionCache();
}
/**
* Reset the internal reflection caches
*
* @return void
*/
public function resetReflectionCache() {
$this->_reflectionPropertyCache = array();
$this->_reflectionMethodCache = array();
$this->_reflectionInstanceCache = array();
}
/**
* Map a instance of a object to its class name
*
* @param Object $instance
* @return void
*/
public function setReflectionClassInstance($instance, $class = null) {
$class = $class ?: get_class($instance);
$this->_reflectionInstanceCache[$class] = $instance;
}
/**
* Get working instance of "$class"
*
* @param string $class
* @return Object
* @throws Exception When the reflection instance cannot be found
*/
public function getReflectionInstance($class) {
$class = $this->_getReflectionTargetClass($class);
if (empty($this->_reflectionInstanceCache[$class])) {
throw new Exception(sprintf('Unable to find instance of %s in the reflection cache. Have you added it using "setReflectionClassInstance"?', $class));
}
return $this->_reflectionInstanceCache[$class];
}
/**
* Helper method to call a protected method
*
* @param string $method
* @param array $args Argument list to call $method with (call_user_func_array style)
* @param string $class Target reflection class
* @return mixed
*/
public function callProtectedMethod($method, $args = array(), $class = null) {
$class = $this->_getReflectionTargetClass($class);
$cacheKey = $class . '_' . $method;
if (!in_array($cacheKey, $this->_reflectionMethodCache)) {
$this->_reflectionMethodCache[$cacheKey] = new ReflectionMethod($class, $method);
$this->_reflectionMethodCache[$cacheKey]->setAccessible(true);
}
return $this->_reflectionMethodCache[$cacheKey]->invokeArgs($this->getReflectionInstance($class), $args);
}
/**
* Helper method to get the value of a protected property
*
* @param string $property
* @param string $class Target reflection class
* @return mixed
*/
public function getProtectedProperty($property, $class = null) {
$Instance = $this->_getReflectionPropertyInstance($property, $class);
return $Instance->getValue($this->getReflectionInstance($class));
}
/**
* Helper method to set the value of a protected property
*
* @param string $property
* @param mixed $value
* @param string $class Target reflection class
* @return mixed
*/
public function setProtectedProperty($property, $value, $class = null) {
$Instance = $this->_getReflectionPropertyInstance($property, $class);
return $Instance->setValue($this->getReflectionInstance($class), $value);
}
/**
* Get a reflection property object
*
* @param string $property
* @param string $class
* @return ReflectionProperty
*/
protected function _getReflectionPropertyInstance($property, $class) {
$class = $this->_getReflectionTargetClass($class);
$cacheKey = $class . '_' . $property;
if (!in_array($cacheKey, $this->_reflectionPropertyCache)) {
$this->_reflectionPropertyCache[$cacheKey] = new ReflectionProperty($class, $property);
$this->_reflectionPropertyCache[$cacheKey]->setAccessible(true);
}
return $this->_reflectionPropertyCache[$cacheKey];
}
/**
* Get the reflection class name
*
* @param string $class
* @return string
* @throws Exception When the reflection target cannot be found
*/
protected function _getReflectionTargetClass($class) {
if (is_object($class)) {
$class = get_class($class);
}
if (!empty($class)) {
return $class;
}
if (isset($this->defaultRelfectionTarget)) {
$class = $this->defaultRelfectionTarget;
if (is_object($class)) {
$class = get_class($class);
}
}
if (empty($class)) {
throw new Exception(sprintf('Unable to find reflection target; have you set $defaultRelfectionTarget or passed in class name?', $class));
}
return $class;
}
}

View File

@ -1,48 +0,0 @@
<?php
App::uses('View', 'View');
App::uses('JsonView', 'View');
/**
* CrudApiView
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*
* @codeCoverageIgnore Backport of 2.4 JsonView aliasing
*/
class CrudJsonView extends JsonView {
/**
* Serialize view vars
*
* @param array $serialize The viewVars that need to be serialized
* @return string The serialized data
*/
protected function _serialize($serialize) {
if (is_array($serialize)) {
$data = array();
foreach ($serialize as $alias => $key) {
if (is_numeric($alias)) {
$alias = $key;
}
if (array_key_exists($key, $this->viewVars)) {
$data[$alias] = $this->viewVars[$key];
}
}
$data = !empty($data) ? $data : null;
} else {
$data = isset($this->viewVars[$serialize]) ? $this->viewVars[$serialize] : null;
}
if (version_compare(PHP_VERSION, '5.4.0', '>=') && Configure::read('debug')) {
return json_encode($data, JSON_PRETTY_PRINT);
}
return json_encode($data);
}
}

View File

@ -1,52 +0,0 @@
<?php
App::uses('View', 'View');
App::uses('XmlView', 'View');
/**
* CrudApiView
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
*
* @codeCoverageIgnore Backport of 2.4 XmlView aliasing
*/
class CrudXmlView extends XmlView {
/**
* Serialize view vars.
*
* @param array $serialize The viewVars that need to be serialized.
* @return string The serialized data
*/
protected function _serialize($serialize) {
$rootNode = isset($this->viewVars['_rootNode']) ? $this->viewVars['_rootNode'] : 'response';
if (is_array($serialize)) {
$data = array($rootNode => array());
foreach ($serialize as $alias => $key) {
if (is_numeric($alias)) {
$alias = $key;
}
$data[$rootNode][$alias] = $this->viewVars[$key];
}
} else {
$data = isset($this->viewVars[$serialize]) ? $this->viewVars[$serialize] : null;
if (is_array($data) && Set::numeric(array_keys($data))) {
$data = array($rootNode => array($serialize => $data));
}
}
$options = array();
if (Configure::read('debug')) {
$options['pretty'] = true;
}
return Xml::fromArray($data, $options)->asXML();
}
}

View File

@ -1,17 +0,0 @@
<h2><?= __d('crud', 'Config'); ?></h2>
<?php
if (empty($crudDebugKitData['action'])) {
$crudDebugKitData['action'] = __d('crud', 'Current action is not handled by Crud');
}
$config = array(
__d('crud', 'Action') => $crudDebugKitData['action'],
__d('crud', 'Component') => $crudDebugKitData['component'],
__d('crud', 'Listeners') => $crudDebugKitData['listeners']
);
echo $this->Toolbar->makeNeatArray($config);
?>
<h2><?= __d('crud', 'Events triggered'); ?></h2>
<?php
echo $this->Toolbar->makeNeatArray($crudDebugKitData['events']);

View File

@ -1,57 +0,0 @@
{
"name":"friendsofcake/crud",
"version": "3.0.10",
"description":"CakePHP Application development on steroids - rapid prototyping / scaffolding & production ready code - XML / JSON APIs and more",
"type":"cakephp-plugin",
"keywords":[
"cakephp",
"crud",
"create",
"retrieve",
"update",
"delete",
"bake",
"cake",
"scaffold",
"scaffolding"
],
"homepage":"https://github.com/FriendsOfCake/crud",
"license":"MIT",
"authors":[
{
"name":"Christian Winther",
"role":"Author",
"homepage":"http://cakephp.nu/"
},
{
"name":"José Lorenzo Rodríguez",
"role":"Contributor",
"homepage":"https://github.com/lorenzo"
},
{
"name":"Andy Dawson",
"role":"Contributor",
"homepage":"https://github.com/ad7six"
},
{
"name":"ADmad",
"role":"Contributor",
"homepage":"https://github.com/admad"
}
],
"suggest":{
"cakedc/search":"If you want to use the Search Listener"
},
"support":{
"source":"https://github.com/FriendsOfCake/crud",
"issues":"https://github.com/FriendsOfCake/crud/issues",
"wiki":"http://cakephp.nu/cakephp-crud/",
"irc":"irc://irc.freenode.org/friendsofcake"
},
"require":{
"composer/installers":"*"
},
"extra": {
"installer-name": "Crud"
}
}