Convert Crud to submodule
This commit is contained in:
parent
dd9603f70f
commit
d915d859ed
|
@ -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
|
|
@ -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
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
:major: 3
|
||||
:minor: 0
|
||||
:patch: 10
|
||||
:special: ''
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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']);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
|
||||
}
|
|
@ -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']);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
|
||||
}
|
|
@ -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']);
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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(
|
||||
|
||||
);
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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']);
|
|
@ -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"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue