From dd9603f70fbffd51614db0936df02970b172f20f Mon Sep 17 00:00:00 2001
From: Ubuntu
Date: Thu, 11 Jun 2015 02:58:58 +0000
Subject: [PATCH 1/2] Merged Angular UI branch API to master
---
.../CMakeDirectoryInformation.cmake | 16 +
web/api/CMakeFiles/progress.marks | 1 +
web/api/app/Config/bootstrap.php | 1 +
web/api/app/Config/core.php | 387 +++++
web/api/app/Config/routes.php | 8 +
web/api/app/Controller/AppController.php | 18 +-
.../Component/ConfigParserComponent.php | 2 +-
.../Controller/Component/FilterComponent.php | 34 +
.../Controller/Component/ImageComponent.php | 89 +
.../Controller/Component/ScalerComponent.php | 27 +
web/api/app/Controller/ConfigsController.php | 69 +-
web/api/app/Controller/ControlsController.php | 59 +
web/api/app/Controller/EventsController.php | 189 ++-
web/api/app/Controller/HostController.php | 110 ++
web/api/app/Controller/LogsController.php | 14 +-
web/api/app/Controller/MonitorsController.php | 80 +
web/api/app/Controller/StatesController.php | 117 ++
.../app/Controller/ZonePresetsController.php | 99 ++
web/api/app/Controller/ZonesController.php | 87 +-
web/api/app/Model/Config.php | 16 +-
web/api/app/Model/Control.php | 54 +
web/api/app/Model/Frame.php | 2 +
web/api/app/Model/Host.php | 9 +
web/api/app/Model/Monitor.php | 2 +
web/api/app/Model/State.php | 30 +
web/api/app/Model/Zone.php | 1 +
web/api/app/Model/ZonePreset.php | 30 +
web/api/app/Plugin/Crud/.editorconfig | 16 +
web/api/app/Plugin/Crud/.semver | 5 +
web/api/app/Plugin/Crud/.travis.yml | 38 +
web/api/app/Plugin/Crud/CONTRIBUTING.md | 69 +
.../Console/Command/TranslationsShell.php | 310 ++++
.../Controller/Component/CrudComponent.php | 845 ++++++++++
.../Controller/Crud/Action/AddCrudAction.php | 131 ++
.../Crud/Action/DeleteCrudAction.php | 108 ++
.../Controller/Crud/Action/EditCrudAction.php | 293 ++++
.../Crud/Action/IndexCrudAction.php | 120 ++
.../Controller/Crud/Action/ViewCrudAction.php | 94 ++
.../Crud/Controller/Crud/CrudAction.php | 441 +++++
.../Crud/Controller/Crud/CrudBaseObject.php | 277 ++++
.../Crud/Controller/Crud/CrudListener.php | 67 +
.../Crud/Controller/Crud/CrudSubject.php | 157 ++
.../Crud/Listener/ApiFieldFilterListener.php | 348 ++++
.../Controller/Crud/Listener/ApiListener.php | 443 +++++
.../Crud/Listener/ApiPaginationListener.php | 59 +
.../Crud/Listener/ApiQueryLogListener.php | 100 ++
.../Listener/ApiTransformationListener.php | 325 ++++
.../Crud/Listener/DebugKitListener.php | 157 ++
.../Crud/Listener/RedirectListener.php | 176 ++
.../Crud/Listener/RelatedModelsListener.php | 210 +++
.../Crud/Listener/ScaffoldListener.php | 159 ++
.../Crud/Listener/SearchListener.php | 211 +++
.../Crud/Error/CrudExceptionRenderer.php | 132 ++
.../Exception/CrudValidationException.php | 98 ++
web/api/app/Plugin/Crud/LICENSE.txt | 21 +
.../Plugin/Crud/Lib/CrudControllerTrait.php | 64 +
.../app/Plugin/Crud/Lib/Panel/CrudPanel.php | 125 ++
web/api/app/Plugin/Crud/README.md | 59 +
.../Crud/Routing/Filter/FakeHeadFilter.php | 60 +
.../Crud/Routing/Filter/HttpMethodFilter.php | 104 ++
.../app/Plugin/Crud/Test/Case/AllCrudTest.php | 34 +
.../Console/Command/TranslationsShellTest.php | 449 +++++
.../Component/CrudComponentTest.php | 1174 +++++++++++++
.../Test/Case/Controller/Component/models.php | 58 +
.../Crud/Action/AddCrudActionTest.php | 306 ++++
.../Crud/Action/DeleteCrudActionTest.php | 431 +++++
.../Crud/Action/EditCrudActionTest.php | 1104 +++++++++++++
.../Crud/Action/IndexCrudActionTest.php | 341 ++++
.../Crud/Action/ViewCrudActionTest.php | 365 +++++
.../Case/Controller/Crud/CrudActionTest.php | 989 +++++++++++
.../Case/Controller/Crud/CrudListenerTest.php | 19 +
.../Case/Controller/Crud/CrudSubjectTest.php | 83 +
.../Listener/ApiFieldFilterListenerTest.php | 438 +++++
.../Crud/Listener/ApiListenerTest.php | 1450 +++++++++++++++++
.../Listener/ApiPaginationListenerTest.php | 167 ++
.../Crud/Listener/ApiQueryLogListenerTest.php | 196 +++
.../ApiTransformationListenerTest.php | 1441 ++++++++++++++++
.../Crud/Listener/RedirectListenerTest.php | 529 ++++++
.../Listener/RelatedModelsListenerTest.php | 826 ++++++++++
.../Crud/Listener/ScaffoldListenerTest.php | 323 ++++
.../Crud/Listener/SearchListenerTest.php | 719 ++++++++
.../Case/Error/CrudExceptionRendererTest.php | 528 ++++++
.../Test/Case/Lib/Panel/CrudPanelTest.php | 89 +
.../Routing/Filter/FakeHeadFilterTest.php | 51 +
.../Routing/Filter/HttpMethodFilterTest.php | 207 +++
.../Crud/Test/Fixture/PostsTagFixture.php | 35 +
.../Test/Support/CrudControllerTestCase.php | 169 ++
.../Plugin/Crud/Test/Support/CrudTestCase.php | 168 ++
web/api/app/Plugin/Crud/View/CrudJsonView.php | 48 +
web/api/app/Plugin/Crud/View/CrudXmlView.php | 52 +
.../Plugin/Crud/View/Elements/crud_panel.ctp | 17 +
web/api/app/Plugin/Crud/composer.json | 57 +
web/api/app/View/Events/json/index.ctp | 6 +-
web/api/app/View/Logs/json/index.ctp | 5 +
web/api/app/View/View/Configs/json/edit.ctp | 1 +
web/api/app/View/View/Configs/json/index.ctp | 1 +
web/api/app/View/View/Configs/json/view.ctp | 1 +
web/api/app/View/View/Configs/xml/index.ctp | 2 +
web/api/app/View/View/Configs/xml/view.ctp | 2 +
web/api/app/View/View/Elements/empty | 0
web/api/app/View/View/Emails/html/default.ctp | 25 +
web/api/app/View/View/Emails/text/default.ctp | 19 +
web/api/app/View/View/Errors/error400.ctp | 31 +
web/api/app/View/View/Errors/error500.ctp | 28 +
web/api/app/View/View/Events/json/index.ctp | 5 +
web/api/app/View/View/Events/json/view.ctp | 1 +
web/api/app/View/View/Events/xml/index.ctp | 2 +
web/api/app/View/View/Events/xml/view.ctp | 2 +
web/api/app/View/View/Helper/AppHelper.php | 33 +
.../View/View/Layouts/Emails/html/default.ctp | 29 +
.../View/View/Layouts/Emails/text/default.ctp | 21 +
web/api/app/View/View/Layouts/ajax.ctp | 19 +
web/api/app/View/View/Layouts/default.ctp | 65 +
web/api/app/View/View/Layouts/error.ctp | 61 +
web/api/app/View/View/Layouts/flash.ctp | 37 +
web/api/app/View/View/Layouts/js/default.ctp | 2 +
web/api/app/View/View/Layouts/rss/default.ctp | 14 +
web/api/app/View/View/Layouts/xml/default.ctp | 1 +
web/api/app/View/View/Logs/json/index.ctp | 5 +
web/api/app/View/View/Monitors/json/edit.ctp | 2 +
web/api/app/View/View/Monitors/json/index.ctp | 1 +
web/api/app/View/View/Monitors/json/view.ctp | 1 +
web/api/app/View/View/Monitors/xml/edit.ctp | 2 +
web/api/app/View/View/Monitors/xml/index.ctp | 2 +
web/api/app/View/View/Monitors/xml/view.ctp | 2 +
web/api/app/View/View/Pages/home.ctp | 233 +++
web/api/app/View/View/Scaffolds/empty | 0
web/api/app/vendor/autoload.php | 7 +
web/api/app/vendor/composer/ClassLoader.php | 413 +++++
.../app/vendor/composer/autoload_classmap.php | 9 +
.../vendor/composer/autoload_namespaces.php | 10 +
web/api/app/vendor/composer/autoload_psr4.php | 9 +
web/api/app/vendor/composer/autoload_real.php | 50 +
web/api/app/vendor/composer/installed.json | 167 ++
.../vendor/composer/installers/.editorconfig | 10 +
.../app/vendor/composer/installers/.gitignore | 3 +
.../vendor/composer/installers/.travis.yml | 14 +
.../app/vendor/composer/installers/LICENSE | 19 +
.../app/vendor/composer/installers/README.md | 191 +++
.../vendor/composer/installers/composer.json | 77 +
.../composer/installers/phpunit.xml.dist | 25 +
.../src/Composer/Installers/AglInstaller.php | 21 +
.../Composer/Installers/AimeosInstaller.php | 9 +
.../Installers/AnnotateCmsInstaller.php | 11 +
.../Composer/Installers/AsgardInstaller.php | 45 +
.../src/Composer/Installers/BaseInstaller.php | 131 ++
.../Composer/Installers/BitrixInstaller.php | 11 +
.../Composer/Installers/CakePHPInstaller.php | 78 +
.../src/Composer/Installers/ChefInstaller.php | 11 +
.../Installers/ClanCatsFrameworkInstaller.php | 10 +
.../Installers/CodeIgniterInstaller.php | 11 +
.../Installers/Concrete5Installer.php | 12 +
.../Composer/Installers/CraftInstaller.php | 9 +
.../Composer/Installers/CroogoInstaller.php | 21 +
.../Composer/Installers/DokuWikiInstaller.php | 50 +
.../Composer/Installers/DolibarrInstaller.php | 16 +
.../Composer/Installers/DrupalInstaller.php | 14 +
.../src/Composer/Installers/ElggInstaller.php | 9 +
.../src/Composer/Installers/FuelInstaller.php | 11 +
.../Composer/Installers/FuelphpInstaller.php | 9 +
.../src/Composer/Installers/GravInstaller.php | 30 +
.../Composer/Installers/HuradInstaller.php | 25 +
.../src/Composer/Installers/Installer.php | 163 ++
.../Composer/Installers/JoomlaInstaller.php | 15 +
.../Composer/Installers/KirbyInstaller.php | 9 +
.../Composer/Installers/KohanaInstaller.php | 9 +
.../Composer/Installers/LaravelInstaller.php | 9 +
.../Composer/Installers/LithiumInstaller.php | 10 +
.../Installers/MODULEWorkInstaller.php | 9 +
.../Composer/Installers/MODXEvoInstaller.php | 16 +
.../Composer/Installers/MagentoInstaller.php | 11 +
.../src/Composer/Installers/MakoInstaller.php | 9 +
.../Installers/MediaWikiInstaller.php | 50 +
.../Installers/MicroweberInstaller.php | 111 ++
.../Composer/Installers/MoodleInstaller.php | 47 +
.../Composer/Installers/OctoberInstaller.php | 46 +
.../src/Composer/Installers/OxidInstaller.php | 11 +
.../src/Composer/Installers/PPIInstaller.php | 9 +
.../Composer/Installers/PhpBBInstaller.php | 11 +
.../Composer/Installers/PimcoreInstaller.php | 21 +
.../Composer/Installers/PiwikInstaller.php | 32 +
.../Installers/PrestashopInstaller.php | 10 +
.../Composer/Installers/PuppetInstaller.php | 11 +
.../Composer/Installers/RedaxoInstaller.php | 10 +
.../Installers/RoundcubeInstaller.php | 22 +
.../src/Composer/Installers/SMFInstaller.php | 10 +
.../Composer/Installers/ShopwareInstaller.php | 58 +
.../Installers/SilverStripeInstaller.php | 36 +
.../Composer/Installers/Symfony1Installer.php | 26 +
.../Composer/Installers/TYPO3CmsInstaller.php | 14 +
.../Installers/TYPO3FlowInstaller.php | 38 +
.../Composer/Installers/TheliaInstaller.php | 12 +
.../src/Composer/Installers/TuskInstaller.php | 14 +
.../Composer/Installers/WHMCSInstaller.php | 10 +
.../Composer/Installers/WolfCMSInstaller.php | 9 +
.../Installers/WordPressInstaller.php | 11 +
.../src/Composer/Installers/ZendInstaller.php | 11 +
.../Composer/Installers/ZikulaInstaller.php | 10 +
.../composer/installers/src/bootstrap.php | 13 +
.../Installers/Test/AsgardInstallerTest.php | 61 +
.../Installers/Test/CakePHPInstallerTest.php | 115 ++
.../Installers/Test/DokuWikiInstallerTest.php | 89 +
.../Installers/Test/GravInstallerTest.php | 63 +
.../Installers/Test/InstallerTest.php | 422 +++++
.../Test/MediaWikiInstallerTest.php | 66 +
.../Installers/Test/OctoberInstallerTest.php | 66 +
.../Installers/Test/PimcoreInstallerTest.php | 44 +
.../Installers/Test/PiwikInstallerTest.php | 63 +
.../Composer/Installers/Test/TestCase.php | 64 +
.../composer/installers/tests/bootstrap.php | 4 +
web/api/app/webroot/.htaccess | 1 +
web/api/cmake_install.cmake | 34 +
212 files changed, 24358 insertions(+), 102 deletions(-)
create mode 100644 web/api/CMakeFiles/CMakeDirectoryInformation.cmake
create mode 100644 web/api/CMakeFiles/progress.marks
create mode 100644 web/api/app/Config/core.php
create mode 100644 web/api/app/Controller/Component/FilterComponent.php
create mode 100644 web/api/app/Controller/Component/ImageComponent.php
create mode 100644 web/api/app/Controller/Component/ScalerComponent.php
create mode 100644 web/api/app/Controller/ControlsController.php
create mode 100644 web/api/app/Controller/HostController.php
create mode 100644 web/api/app/Controller/StatesController.php
create mode 100644 web/api/app/Controller/ZonePresetsController.php
create mode 100644 web/api/app/Model/Control.php
create mode 100644 web/api/app/Model/Host.php
create mode 100644 web/api/app/Model/State.php
create mode 100644 web/api/app/Model/ZonePreset.php
create mode 100644 web/api/app/Plugin/Crud/.editorconfig
create mode 100644 web/api/app/Plugin/Crud/.semver
create mode 100644 web/api/app/Plugin/Crud/.travis.yml
create mode 100644 web/api/app/Plugin/Crud/CONTRIBUTING.md
create mode 100644 web/api/app/Plugin/Crud/Console/Command/TranslationsShell.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Component/CrudComponent.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Action/AddCrudAction.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Action/DeleteCrudAction.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Action/EditCrudAction.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Action/IndexCrudAction.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Action/ViewCrudAction.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/CrudAction.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/CrudBaseObject.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/CrudListener.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/CrudSubject.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiFieldFilterListener.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiListener.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiPaginationListener.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiQueryLogListener.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiTransformationListener.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/DebugKitListener.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/RedirectListener.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/RelatedModelsListener.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/ScaffoldListener.php
create mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/SearchListener.php
create mode 100644 web/api/app/Plugin/Crud/Error/CrudExceptionRenderer.php
create mode 100644 web/api/app/Plugin/Crud/Error/Exception/CrudValidationException.php
create mode 100644 web/api/app/Plugin/Crud/LICENSE.txt
create mode 100644 web/api/app/Plugin/Crud/Lib/CrudControllerTrait.php
create mode 100644 web/api/app/Plugin/Crud/Lib/Panel/CrudPanel.php
create mode 100644 web/api/app/Plugin/Crud/README.md
create mode 100644 web/api/app/Plugin/Crud/Routing/Filter/FakeHeadFilter.php
create mode 100644 web/api/app/Plugin/Crud/Routing/Filter/HttpMethodFilter.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/AllCrudTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Console/Command/TranslationsShellTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Component/CrudComponentTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Component/models.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/AddCrudActionTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/DeleteCrudActionTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/EditCrudActionTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/IndexCrudActionTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/ViewCrudActionTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudActionTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudListenerTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudSubjectTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiFieldFilterListenerTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiListenerTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiPaginationListenerTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiQueryLogListenerTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiTransformationListenerTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/RedirectListenerTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/RelatedModelsListenerTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ScaffoldListenerTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/SearchListenerTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Error/CrudExceptionRendererTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Lib/Panel/CrudPanelTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Routing/Filter/FakeHeadFilterTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Case/Routing/Filter/HttpMethodFilterTest.php
create mode 100644 web/api/app/Plugin/Crud/Test/Fixture/PostsTagFixture.php
create mode 100644 web/api/app/Plugin/Crud/Test/Support/CrudControllerTestCase.php
create mode 100644 web/api/app/Plugin/Crud/Test/Support/CrudTestCase.php
create mode 100644 web/api/app/Plugin/Crud/View/CrudJsonView.php
create mode 100644 web/api/app/Plugin/Crud/View/CrudXmlView.php
create mode 100644 web/api/app/Plugin/Crud/View/Elements/crud_panel.ctp
create mode 100644 web/api/app/Plugin/Crud/composer.json
create mode 100644 web/api/app/View/Logs/json/index.ctp
create mode 100644 web/api/app/View/View/Configs/json/edit.ctp
create mode 100644 web/api/app/View/View/Configs/json/index.ctp
create mode 100644 web/api/app/View/View/Configs/json/view.ctp
create mode 100644 web/api/app/View/View/Configs/xml/index.ctp
create mode 100644 web/api/app/View/View/Configs/xml/view.ctp
create mode 100644 web/api/app/View/View/Elements/empty
create mode 100644 web/api/app/View/View/Emails/html/default.ctp
create mode 100644 web/api/app/View/View/Emails/text/default.ctp
create mode 100644 web/api/app/View/View/Errors/error400.ctp
create mode 100644 web/api/app/View/View/Errors/error500.ctp
create mode 100644 web/api/app/View/View/Events/json/index.ctp
create mode 100644 web/api/app/View/View/Events/json/view.ctp
create mode 100644 web/api/app/View/View/Events/xml/index.ctp
create mode 100644 web/api/app/View/View/Events/xml/view.ctp
create mode 100644 web/api/app/View/View/Helper/AppHelper.php
create mode 100644 web/api/app/View/View/Layouts/Emails/html/default.ctp
create mode 100644 web/api/app/View/View/Layouts/Emails/text/default.ctp
create mode 100644 web/api/app/View/View/Layouts/ajax.ctp
create mode 100644 web/api/app/View/View/Layouts/default.ctp
create mode 100644 web/api/app/View/View/Layouts/error.ctp
create mode 100644 web/api/app/View/View/Layouts/flash.ctp
create mode 100644 web/api/app/View/View/Layouts/js/default.ctp
create mode 100644 web/api/app/View/View/Layouts/rss/default.ctp
create mode 100644 web/api/app/View/View/Layouts/xml/default.ctp
create mode 100644 web/api/app/View/View/Logs/json/index.ctp
create mode 100644 web/api/app/View/View/Monitors/json/edit.ctp
create mode 100644 web/api/app/View/View/Monitors/json/index.ctp
create mode 100644 web/api/app/View/View/Monitors/json/view.ctp
create mode 100644 web/api/app/View/View/Monitors/xml/edit.ctp
create mode 100644 web/api/app/View/View/Monitors/xml/index.ctp
create mode 100644 web/api/app/View/View/Monitors/xml/view.ctp
create mode 100644 web/api/app/View/View/Pages/home.ctp
create mode 100644 web/api/app/View/View/Scaffolds/empty
create mode 100644 web/api/app/vendor/autoload.php
create mode 100644 web/api/app/vendor/composer/ClassLoader.php
create mode 100644 web/api/app/vendor/composer/autoload_classmap.php
create mode 100644 web/api/app/vendor/composer/autoload_namespaces.php
create mode 100644 web/api/app/vendor/composer/autoload_psr4.php
create mode 100644 web/api/app/vendor/composer/autoload_real.php
create mode 100644 web/api/app/vendor/composer/installed.json
create mode 100644 web/api/app/vendor/composer/installers/.editorconfig
create mode 100644 web/api/app/vendor/composer/installers/.gitignore
create mode 100644 web/api/app/vendor/composer/installers/.travis.yml
create mode 100644 web/api/app/vendor/composer/installers/LICENSE
create mode 100644 web/api/app/vendor/composer/installers/README.md
create mode 100644 web/api/app/vendor/composer/installers/composer.json
create mode 100644 web/api/app/vendor/composer/installers/phpunit.xml.dist
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/AglInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/AimeosInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/AsgardInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/BaseInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/CakePHPInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/ChefInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/Concrete5Installer.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/CraftInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/CroogoInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/DokuWikiInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/DolibarrInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/ElggInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/FuelInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/FuelphpInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/GravInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/HuradInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/Installer.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/JoomlaInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/KirbyInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/KohanaInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/LaravelInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/LithiumInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/MODXEvoInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/MagentoInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/MakoInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/MediaWikiInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/OctoberInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/OxidInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/PPIInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/PhpBBInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/PimcoreInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/PiwikInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/PrestashopInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/PuppetInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/RedaxoInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/RoundcubeInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/SMFInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/ShopwareInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/SilverStripeInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/Symfony1Installer.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3FlowInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/TheliaInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/TuskInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/WolfCMSInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/WordPressInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/ZendInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/Composer/Installers/ZikulaInstaller.php
create mode 100644 web/api/app/vendor/composer/installers/src/bootstrap.php
create mode 100644 web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/AsgardInstallerTest.php
create mode 100644 web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/CakePHPInstallerTest.php
create mode 100644 web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/DokuWikiInstallerTest.php
create mode 100644 web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/GravInstallerTest.php
create mode 100644 web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/InstallerTest.php
create mode 100644 web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/MediaWikiInstallerTest.php
create mode 100644 web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/OctoberInstallerTest.php
create mode 100644 web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PimcoreInstallerTest.php
create mode 100644 web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PiwikInstallerTest.php
create mode 100644 web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/TestCase.php
create mode 100644 web/api/app/vendor/composer/installers/tests/bootstrap.php
create mode 100644 web/api/cmake_install.cmake
diff --git a/web/api/CMakeFiles/CMakeDirectoryInformation.cmake b/web/api/CMakeFiles/CMakeDirectoryInformation.cmake
new file mode 100644
index 000000000..3d1e4eadd
--- /dev/null
+++ b/web/api/CMakeFiles/CMakeDirectoryInformation.cmake
@@ -0,0 +1,16 @@
+# CMAKE generated file: DO NOT EDIT!
+# Generated by "Unix Makefiles" Generator, CMake Version 2.8
+
+# Relative path conversion top directories.
+SET(CMAKE_RELATIVE_PATH_TOP_SOURCE "/home/ubuntu/zm/ZoneMinder")
+SET(CMAKE_RELATIVE_PATH_TOP_BINARY "/home/ubuntu/zm/ZoneMinder")
+
+# Force unix paths in dependencies.
+SET(CMAKE_FORCE_UNIX_PATHS 1)
+
+
+# The C and CXX include file regular expressions for this directory.
+SET(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$")
+SET(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$")
+SET(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN})
+SET(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN})
diff --git a/web/api/CMakeFiles/progress.marks b/web/api/CMakeFiles/progress.marks
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/web/api/CMakeFiles/progress.marks
@@ -0,0 +1 @@
+0
diff --git a/web/api/app/Config/bootstrap.php b/web/api/app/Config/bootstrap.php
index b8932f3a6..e4102d62b 100644
--- a/web/api/app/Config/bootstrap.php
+++ b/web/api/app/Config/bootstrap.php
@@ -69,6 +69,7 @@ Cache::config('default', array('engine' => 'File'));
* CakePlugin::load('DebugKit'); //Loads a single plugin named DebugKit
*
*/
+CakePlugin::load('Crud');
/**
* You can attach event listeners to the request lifecycle as Dispatcher Filter. By default CakePHP bundles two filters:
diff --git a/web/api/app/Config/core.php b/web/api/app/Config/core.php
new file mode 100644
index 000000000..1e1b11bc7
--- /dev/null
+++ b/web/api/app/Config/core.php
@@ -0,0 +1,387 @@
+ 0
+ * and log errors with CakeLog when debug = 0.
+ *
+ * Options:
+ *
+ * - `handler` - callback - The callback to handle errors. You can set this to any callable type,
+ * including anonymous functions.
+ * Make sure you add App::uses('MyHandler', 'Error'); when using a custom handler class
+ * - `level` - integer - The level of errors you are interested in capturing.
+ * - `trace` - boolean - Include stack traces for errors in log files.
+ *
+ * @see ErrorHandler for more information on error handling and configuration.
+ */
+ Configure::write('Error', array(
+ 'handler' => 'ErrorHandler::handleError',
+ 'level' => E_ALL & ~E_DEPRECATED,
+ 'trace' => true
+ ));
+
+/**
+ * Configure the Exception handler used for uncaught exceptions. By default,
+ * ErrorHandler::handleException() is used. It will display a HTML page for the exception, and
+ * while debug > 0, framework errors like Missing Controller will be displayed. When debug = 0,
+ * framework errors will be coerced into generic HTTP errors.
+ *
+ * Options:
+ *
+ * - `handler` - callback - The callback to handle exceptions. You can set this to any callback type,
+ * including anonymous functions.
+ * Make sure you add App::uses('MyHandler', 'Error'); when using a custom handler class
+ * - `renderer` - string - The class responsible for rendering uncaught exceptions. If you choose a custom class you
+ * should place the file for that class in app/Lib/Error. This class needs to implement a render method.
+ * - `log` - boolean - Should Exceptions be logged?
+ * - `skipLog` - array - list of exceptions to skip for logging. Exceptions that
+ * extend one of the listed exceptions will also be skipped for logging.
+ * Example: `'skipLog' => array('NotFoundException', 'UnauthorizedException')`
+ *
+ * @see ErrorHandler for more information on exception handling and configuration.
+ */
+ Configure::write('Exception', array(
+ 'handler' => 'ErrorHandler::handleException',
+ 'renderer' => 'ExceptionRenderer',
+ 'log' => true
+ ));
+
+/**
+ * Application wide charset encoding
+ */
+ Configure::write('App.encoding', 'UTF-8');
+
+/**
+ * To configure CakePHP *not* to use mod_rewrite and to
+ * use CakePHP pretty URLs, remove these .htaccess
+ * files:
+ *
+ * /.htaccess
+ * /app/.htaccess
+ * /app/webroot/.htaccess
+ *
+ * And uncomment the App.baseUrl below. But keep in mind
+ * that plugin assets such as images, CSS and JavaScript files
+ * will not work without URL rewriting!
+ * To work around this issue you should either symlink or copy
+ * the plugin assets into you app's webroot directory. This is
+ * recommended even when you are using mod_rewrite. Handling static
+ * assets through the Dispatcher is incredibly inefficient and
+ * included primarily as a development convenience - and
+ * thus not recommended for production applications.
+ */
+ //Configure::write('App.baseUrl', env('SCRIPT_NAME'));
+
+/**
+ * To configure CakePHP to use a particular domain URL
+ * for any URL generation inside the application, set the following
+ * configuration variable to the http(s) address to your domain. This
+ * will override the automatic detection of full base URL and can be
+ * useful when generating links from the CLI (e.g. sending emails)
+ */
+ //Configure::write('App.fullBaseUrl', 'http://example.com');
+
+/**
+ * Web path to the public images directory under webroot.
+ * If not set defaults to 'img/'
+ */
+ //Configure::write('App.imageBaseUrl', 'img/');
+
+/**
+ * Web path to the CSS files directory under webroot.
+ * If not set defaults to 'css/'
+ */
+ //Configure::write('App.cssBaseUrl', 'css/');
+
+/**
+ * Web path to the js files directory under webroot.
+ * If not set defaults to 'js/'
+ */
+ //Configure::write('App.jsBaseUrl', 'js/');
+
+/**
+ * Uncomment the define below to use CakePHP prefix routes.
+ *
+ * The value of the define determines the names of the routes
+ * and their associated controller actions:
+ *
+ * Set to an array of prefixes you want to use in your application. Use for
+ * admin or other prefixed routes.
+ *
+ * Routing.prefixes = array('admin', 'manager');
+ *
+ * Enables:
+ * `admin_index()` and `/admin/controller/index`
+ * `manager_index()` and `/manager/controller/index`
+ *
+ */
+ //Configure::write('Routing.prefixes', array('admin'));
+
+/**
+ * Turn off all caching application-wide.
+ *
+ */
+ //Configure::write('Cache.disable', true);
+
+/**
+ * Enable cache checking.
+ *
+ * If set to true, for view caching you must still use the controller
+ * public $cacheAction inside your controllers to define caching settings.
+ * You can either set it controller-wide by setting public $cacheAction = true,
+ * or in each action using $this->cacheAction = true.
+ *
+ */
+ //Configure::write('Cache.check', true);
+
+/**
+ * Enable cache view prefixes.
+ *
+ * If set it will be prepended to the cache name for view file caching. This is
+ * helpful if you deploy the same application via multiple subdomains and languages,
+ * for instance. Each version can then have its own view cache namespace.
+ * Note: The final cache file name will then be `prefix_cachefilename`.
+ */
+ //Configure::write('Cache.viewPrefix', 'prefix');
+
+/**
+ * Session configuration.
+ *
+ * Contains an array of settings to use for session configuration. The defaults key is
+ * used to define a default preset to use for sessions, any settings declared here will override
+ * the settings of the default config.
+ *
+ * ## Options
+ *
+ * - `Session.cookie` - The name of the cookie to use. Defaults to 'CAKEPHP'
+ * - `Session.timeout` - The number of minutes you want sessions to live for. This timeout is handled by CakePHP
+ * - `Session.cookieTimeout` - The number of minutes you want session cookies to live for.
+ * - `Session.checkAgent` - Do you want the user agent to be checked when starting sessions? You might want to set the
+ * value to false, when dealing with older versions of IE, Chrome Frame or certain web-browsing devices and AJAX
+ * - `Session.defaults` - The default configuration set to use as a basis for your session.
+ * There are four builtins: php, cake, cache, database.
+ * - `Session.handler` - Can be used to enable a custom session handler. Expects an array of callables,
+ * that can be used with `session_save_handler`. Using this option will automatically add `session.save_handler`
+ * to the ini array.
+ * - `Session.autoRegenerate` - Enabling this setting, turns on automatic renewal of sessions, and
+ * sessionids that change frequently. See CakeSession::$requestCountdown.
+ * - `Session.ini` - An associative array of additional ini values to set.
+ *
+ * The built in defaults are:
+ *
+ * - 'php' - Uses settings defined in your php.ini.
+ * - 'cake' - Saves session files in CakePHP's /tmp directory.
+ * - 'database' - Uses CakePHP's database sessions.
+ * - 'cache' - Use the Cache class to save sessions.
+ *
+ * To define a custom session handler, save it at /app/Model/Datasource/Session/.php.
+ * Make sure the class implements `CakeSessionHandlerInterface` and set Session.handler to
+ *
+ * To use database sessions, run the app/Config/Schema/sessions.php schema using
+ * the cake shell command: cake schema create Sessions
+ *
+ */
+ Configure::write('Session', array(
+ 'defaults' => 'php'
+ ));
+
+/**
+ * A random string used in security hashing methods.
+ */
+ Configure::write('Security.salt', 'y8s4nPwjdjrmAGRxtnbxWhOknOBX6y1etr6RD8XM');
+
+/**
+ * A random numeric string (digits only) used to encrypt/decrypt strings.
+ */
+ Configure::write('Security.cipherSeed', '28284715058574819699789789248');
+
+/**
+ * Apply timestamps with the last modified time to static assets (js, css, images).
+ * Will append a query string parameter containing the time the file was modified. This is
+ * useful for invalidating browser caches.
+ *
+ * Set to `true` to apply timestamps when debug > 0. Set to 'force' to always enable
+ * timestamping regardless of debug value.
+ */
+ //Configure::write('Asset.timestamp', true);
+
+/**
+ * Compress CSS output by removing comments, whitespace, repeating tags, etc.
+ * This requires a/var/cache directory to be writable by the web server for caching.
+ * and /vendors/csspp/csspp.php
+ *
+ * To use, prefix the CSS link URL with '/ccss/' instead of '/css/' or use HtmlHelper::css().
+ */
+ //Configure::write('Asset.filter.css', 'css.php');
+
+/**
+ * Plug in your own custom JavaScript compressor by dropping a script in your webroot to handle the
+ * output, and setting the config below to the name of the script.
+ *
+ * To use, prefix your JavaScript link URLs with '/cjs/' instead of '/js/' or use JsHelper::link().
+ */
+ //Configure::write('Asset.filter.js', 'custom_javascript_output_filter.php');
+
+/**
+ * The class name and database used in CakePHP's
+ * access control lists.
+ */
+ Configure::write('Acl.classname', 'DbAcl');
+ Configure::write('Acl.database', 'default');
+
+/**
+ * Uncomment this line and correct your server timezone to fix
+ * any date & time related errors.
+ */
+ //date_default_timezone_set('UTC');
+
+/**
+ * `Config.timezone` is available in which you can set users' timezone string.
+ * If a method of CakeTime class is called with $timezone parameter as null and `Config.timezone` is set,
+ * then the value of `Config.timezone` will be used. This feature allows you to set users' timezone just
+ * once instead of passing it each time in function calls.
+ */
+ //Configure::write('Config.timezone', 'Europe/Paris');
+
+/**
+ *
+ * Cache Engine Configuration
+ * Default settings provided below
+ *
+ * File storage engine.
+ *
+ * Cache::config('default', array(
+ * 'engine' => 'File', //[required]
+ * 'duration' => 3600, //[optional]
+ * 'probability' => 100, //[optional]
+ * 'path' => CACHE, //[optional] use system tmp directory - remember to use absolute path
+ * 'prefix' => 'cake_', //[optional] prefix every cache file with this string
+ * 'lock' => false, //[optional] use file locking
+ * 'serialize' => true, //[optional]
+ * 'mask' => 0664, //[optional]
+ * ));
+ *
+ * APC (http://pecl.php.net/package/APC)
+ *
+ * Cache::config('default', array(
+ * 'engine' => 'Apc', //[required]
+ * 'duration' => 3600, //[optional]
+ * 'probability' => 100, //[optional]
+ * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
+ * ));
+ *
+ * Xcache (http://xcache.lighttpd.net/)
+ *
+ * Cache::config('default', array(
+ * 'engine' => 'Xcache', //[required]
+ * 'duration' => 3600, //[optional]
+ * 'probability' => 100, //[optional]
+ * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
+ * 'user' => 'user', //user from xcache.admin.user settings
+ * 'password' => 'password', //plaintext password (xcache.admin.pass)
+ * ));
+ *
+ * Memcached (http://www.danga.com/memcached/)
+ *
+ * Uses the memcached extension. See http://php.net/memcached
+ *
+ * Cache::config('default', array(
+ * 'engine' => 'Memcached', //[required]
+ * 'duration' => 3600, //[optional]
+ * 'probability' => 100, //[optional]
+ * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
+ * 'servers' => array(
+ * '127.0.0.1:11211' // localhost, default port 11211
+ * ), //[optional]
+ * 'persistent' => 'my_connection', // [optional] The name of the persistent connection.
+ * 'compress' => false, // [optional] compress data in Memcached (slower, but uses less memory)
+ * ));
+ *
+ * Wincache (http://php.net/wincache)
+ *
+ * Cache::config('default', array(
+ * 'engine' => 'Wincache', //[required]
+ * 'duration' => 3600, //[optional]
+ * 'probability' => 100, //[optional]
+ * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
+ * ));
+ */
+
+/**
+ * Configure the cache handlers that CakePHP will use for internal
+ * metadata like class maps, and model schema.
+ *
+ * By default File is used, but for improved performance you should use APC.
+ *
+ * Note: 'default' and other application caches should be configured in app/Config/bootstrap.php.
+ * Please check the comments in bootstrap.php for more info on the cache engines available
+ * and their settings.
+ */
+$engine = 'File';
+
+// In development mode, caches should expire quickly.
+$duration = '+999 days';
+if (Configure::read('debug') > 0) {
+ $duration = '+10 seconds';
+}
+
+// Prefix each application on the same server with a different string, to avoid Memcache and APC conflicts.
+$prefix = 'myapp_';
+
+/**
+ * Configure the cache used for general framework caching. Path information,
+ * object listings, and translation cache files are stored with this configuration.
+ */
+Cache::config('_cake_core_', array(
+ 'engine' => $engine,
+ 'prefix' => $prefix . 'cake_core_',
+ 'path' => CACHE . 'persistent' . DS,
+ 'serialize' => ($engine === 'File'),
+ 'duration' => $duration
+));
+
+/**
+ * Configure the cache for model and datasource caches. This cache configuration
+ * is used to store schema descriptions, and table listings in connections.
+ */
+Cache::config('_cake_model_', array(
+ 'engine' => $engine,
+ 'prefix' => $prefix . 'cake_model_',
+ 'path' => CACHE . 'models' . DS,
+ 'serialize' => ($engine === 'File'),
+ 'duration' => $duration
+));
diff --git a/web/api/app/Config/routes.php b/web/api/app/Config/routes.php
index ed2d1f2ca..0f9343644 100644
--- a/web/api/app/Config/routes.php
+++ b/web/api/app/Config/routes.php
@@ -28,6 +28,14 @@
Router::mapResources('configs');
Router::mapResources('events');
Router::mapResources('frames');
+ Router::mapResources('host');
+ Router::mapResources('logs');
+ Router::mapResources('states');
+ Router::mapResources('zonepresets');
+
+ /* Add new API to retrieve camera controls - for PTZ */
+ /* refer to https://github.com/ZoneMinder/ZoneMinder/issues/799#issuecomment-105233112 */
+ Router::mapResources('controls');
Router::parseExtensions();
/**
diff --git a/web/api/app/Controller/AppController.php b/web/api/app/Controller/AppController.php
index a827969e1..f3011e6ff 100644
--- a/web/api/app/Controller/AppController.php
+++ b/web/api/app/Controller/AppController.php
@@ -18,8 +18,8 @@
* @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
-
App::uses('Controller', 'Controller');
+App::uses('CrudControllerTrait', 'Crud.Lib');
/**
* Application Controller
@@ -31,4 +31,20 @@ App::uses('Controller', 'Controller');
* @link http://book.cakephp.org/2.0/en/controllers.html#the-app-controller
*/
class AppController extends Controller {
+ use CrudControllerTrait;
+
+ public $components = [
+ 'RequestHandler',
+ 'Crud.Crud' => [
+ 'actions' => [
+ 'index' => 'Crud.Index',
+ 'add' => 'Crud.Add',
+ 'edit' => 'Crud.Edit',
+ 'view' => 'Crud.View',
+ 'keyvalue' => 'Crud.List',
+ 'category' => 'Crud.Category'
+ ],
+ 'listeners' => ['Api', 'ApiTransformation']
+ ]
+ ];
}
diff --git a/web/api/app/Controller/Component/ConfigParserComponent.php b/web/api/app/Controller/Component/ConfigParserComponent.php
index 18d06fba8..56a2039a0 100644
--- a/web/api/app/Controller/Component/ConfigParserComponent.php
+++ b/web/api/app/Controller/Component/ConfigParserComponent.php
@@ -24,7 +24,7 @@ class ConfigParserComponent extends Component {
public function getInput($name, $type, $id) {
if ($type == 'checkbox') {
- $string = ' ';
+ $string = ' ';
} elseif ($type == 'text') {
$string = ' ';
} elseif ($type == 'textarea') {
diff --git a/web/api/app/Controller/Component/FilterComponent.php b/web/api/app/Controller/Component/FilterComponent.php
new file mode 100644
index 000000000..bf6e423f7
--- /dev/null
+++ b/web/api/app/Controller/Component/FilterComponent.php
@@ -0,0 +1,34 @@
+ $value) {
+ // If the named param contains an array, we want to turn it into an IN condition
+ // Otherwise, we add it right into the $conditions array
+ if (is_array($value)) {
+ $array = array();
+
+ foreach ($value as $term) {
+ array_push($array, $term);
+ }
+
+ $query = array($attribute => $array);
+ array_push($conditions, $query);
+ } else {
+ array_push($conditions, array($attribute => $value));
+ }
+ }
+
+ }
+
+ return $conditions;
+ }
+
+}
+?>
diff --git a/web/api/app/Controller/Component/ImageComponent.php b/web/api/app/Controller/Component/ImageComponent.php
new file mode 100644
index 000000000..147a26dd7
--- /dev/null
+++ b/web/api/app/Controller/Component/ImageComponent.php
@@ -0,0 +1,89 @@
+getEventPath($event);
+
+ $captImage = sprintf( "%0".$config['ZM_EVENT_IMAGE_DIGITS']."d-capture.jpg", $frame['Frame']['FrameId'] );
+ $captPath = $eventPath.'/'.$captImage;
+ $thumbCaptPath = $config['ZM_DIR_IMAGES'].'/'.$event['Event']['Id'].'-'.$captImage;
+
+ $analImage = sprintf( "%0".$config['ZM_EVENT_IMAGE_DIGITS']."d-analyse.jpg", $frame['Frame']['FrameId'] );
+ $analPath = $eventPath.'/'.$analImage;
+ $analFile = $config['ZM_DIR_EVENTS']."/".$analPath;
+ $thumbAnalPath = $config['ZM_DIR_IMAGES'].'/'.$event['Event']['Id'].'-'.$analImage;
+
+ $alarmFrame = $frame['Frame']['Type']=='Alarm';
+
+ $hasAnalImage = $alarmFrame && file_exists( $analFile ) && filesize( $analFile );
+ $isAnalImage = $hasAnalImage && !$captureOnly;
+
+
+ if ( !$config['ZM_WEB_SCALE_THUMBS'] || $scale >= 100 || !function_exists( 'imagecreatefromjpeg' ) ) {
+ $imagePath = $thumbPath = $isAnalImage?$analPath:$captPath;
+ $imageFile = $config['ZM_DIR_EVENTS']."/".$imagePath;
+ $thumbFile = $config['ZM_DIR_EVENTS']."/".$thumbPath;
+ } else {
+ if ( version_compare( phpversion(), "4.3.10", ">=") )
+ $fraction = sprintf( "%.3F", $scale/100 );
+ else
+ $fraction = sprintf( "%.3f", $scale/100 );
+ $scale = (int)round( $scale );
+
+ $thumbCaptPath = preg_replace( "/\.jpg$/", "-$scale.jpg", $thumbCaptPath );
+ $thumbAnalPath = preg_replace( "/\.jpg$/", "-$scale.jpg", $thumbAnalPath );
+
+ if ( $isAnalImage )
+ {
+ $imagePath = $analPath;
+ $thumbPath = $thumbAnalPath;
+ }
+ else
+ {
+ $imagePath = $captPath;
+ $thumbPath = $thumbCaptPath;
+ }
+
+ $imageFile = $config['ZM_DIR_EVENTS']."/".$imagePath;
+ //$thumbFile = ZM_DIR_EVENTS."/".$thumbPath;
+ $thumbFile = $thumbPath;
+ if ( $overwrite || !file_exists( $thumbFile ) || !filesize( $thumbFile ) )
+ {
+ // Get new dimensions
+ list( $imageWidth, $imageHeight ) = getimagesize( $imageFile );
+ $thumbWidth = $imageWidth * $fraction;
+ $thumbHeight = $imageHeight * $fraction;
+
+ // Resample
+ $thumbImage = imagecreatetruecolor( $thumbWidth, $thumbHeight );
+ $image = imagecreatefromjpeg( $imageFile );
+ imagecopyresampled( $thumbImage, $image, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $imageWidth, $imageHeight );
+
+ if ( !imagejpeg( $thumbImage, $thumbFile ) )
+ Error( "Can't create thumbnail '$thumbPath'" );
+ }
+ }
+
+ $imageData = array(
+ 'eventPath' => $eventPath,
+ 'imagePath' => $imagePath,
+ 'thumbPath' => $thumbPath,
+ 'imageFile' => $imageFile,
+ 'thumbFile' => $thumbFile,
+ 'imageClass' => $alarmFrame?"alarm":"normal",
+ 'isAnalImage' => $isAnalImage,
+ 'hasAnalImage' => $hasAnalImage,
+ );
+
+ return( $imageData );
+
+ }
+
+ // Take the StartTime of an Event and return
+ // the path to its location on the filesystem
+ public function getEventPath( $event ) {
+ return $event['Event']['MonitorId'].'/'.strftime( "%y/%m/%d/%H/%M/%S", strtotime($event['Event']['StartTime']) );
+ }
+}
+?>
diff --git a/web/api/app/Controller/Component/ScalerComponent.php b/web/api/app/Controller/Component/ScalerComponent.php
new file mode 100644
index 000000000..30678dad6
--- /dev/null
+++ b/web/api/app/Controller/Component/ScalerComponent.php
@@ -0,0 +1,27 @@
+
diff --git a/web/api/app/Controller/ConfigsController.php b/web/api/app/Controller/ConfigsController.php
index 3198ae58e..651f5724e 100644
--- a/web/api/app/Controller/ConfigsController.php
+++ b/web/api/app/Controller/ConfigsController.php
@@ -14,20 +14,6 @@ class ConfigsController extends AppController {
*/
public $components = array('RequestHandler');
-/**
- * index method
- *
- * @return void
- */
- public function index() {
- $this->Config->recursive = 0;
- $configs = $this->Config->find('all');
- $this->set(array(
- 'configs' => $configs,
- '_serialize' => array('configs')
- ));
- }
-
/**
* view method
*
@@ -47,6 +33,19 @@ class ConfigsController extends AppController {
));
}
+ public function viewByName($name = null) {
+ $config = $this->Config->findByName($name, array('fields' => 'Value'));
+
+ if (!$config) {
+ throw new NotFoundException(__('Invalid config'));
+ }
+
+ $this->set(array(
+ 'config' => $config['Config'],
+ '_serialize' => array('config')
+ ));
+ }
+
/**
* edit method
*
@@ -93,45 +92,19 @@ class ConfigsController extends AppController {
/**
* categories method
*
- * Either return a list of distinct categories
- * Or all configs under a certain category
+ * return a list of distinct categories
*/
public function categories($category = null) {
- if ($category != null) {
- if (!$this->Config->find('first', array( 'conditions' => array('Config.Category' => $category)))) {
- throw new NotFoundException(__('Invalid Config Category'));
- }
-
- $config = $this->Config->find('all', array(
- 'conditions' => array('Config.Category' => $category),
- 'recursive' => 0
- ));
- $this->set(array(
- 'config' => $config,
- '_serialize' => array('config')
- ));
- } else {
- $categories = $this->Config->find('all', array(
- 'fields' => array('DISTINCT Config.Category'),
- 'conditions' => array('Config.Category !=' => 'hidden'),
- 'recursive' => 0
- ));
- $this->set(array(
- 'categories' => $categories,
- '_serialize' => array('categories')
- ));
- }
-
- }
-
- public function keyValue() {
- $keyValues = $this->Config->find('list', array(
- 'fields' => array('Config.Name', 'Config.Value')
+ $categories = $this->Config->find('all', array(
+ 'fields' => array('DISTINCT Config.Category'),
+ 'conditions' => array('Config.Category !=' => 'hidden'),
+ 'recursive' => 0
));
$this->set(array(
- 'keyValues' => $keyValues,
- '_serialize' => array('keyValues')
+ 'categories' => $categories,
+ '_serialize' => array('categories')
));
}
}
+
diff --git a/web/api/app/Controller/ControlsController.php b/web/api/app/Controller/ControlsController.php
new file mode 100644
index 000000000..879142f75
--- /dev/null
+++ b/web/api/app/Controller/ControlsController.php
@@ -0,0 +1,59 @@
+Control->recursive = 0;
+ $controls = $this->Control->find('all');
+ $this->set(array(
+ 'controls' => $controls,
+ '_serialize' => array('controls')
+ ));
+ }
+
+/**
+ * view method
+ *
+ * @throws NotFoundException
+ * @param string $id
+ * @return void
+ */
+ public function view($id = null) {
+ if (!$this->Control->exists($id)) {
+ throw new NotFoundException(__('Invalid control'));
+ }
+ $options = array('conditions' => array('Control.' . $this->Control->primaryKey => $id));
+ $control = $this->Control->find('first', $options);
+ $this->set(array(
+ 'control' => $control,
+ '_serialize' => array('control')
+ ));
+ }
+}
+
diff --git a/web/api/app/Controller/EventsController.php b/web/api/app/Controller/EventsController.php
index 394e61caf..05a280be3 100644
--- a/web/api/app/Controller/EventsController.php
+++ b/web/api/app/Controller/EventsController.php
@@ -12,20 +12,46 @@ class EventsController extends AppController {
*
* @var array
*/
- public $components = array('RequestHandler');
+ public $components = array('RequestHandler', 'Scaler', 'Image', 'Paginator');
/**
* index method
*
* @return void
+ * This also creates a thumbnail for each event.
*/
public function index() {
$this->Event->recursive = -1;
- $events = $this->Event->find('all');
- $this->set(array(
- 'events' => $events,
- '_serialize' => array('events')
+
+ if ($this->request->params['named']) {
+ $this->FilterComponent = $this->Components->load('Filter');
+ $conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
+ } else {
+ $conditions = array();
+ }
+
+ // How many events to return
+ $this->loadModel('Config');
+ $limit = $this->Config->find('list', array(
+ 'conditions' => array('Name' => 'ZM_WEB_EVENTS_PER_PAGE'),
+ 'fields' => array('Name', 'Value')
));
+ $this->Paginator->settings = array(
+ 'limit' => $limit['ZM_WEB_EVENTS_PER_PAGE'],
+ 'order' => array('StartTime', 'MaxScore'),
+ 'paramType' => 'querystring',
+ 'conditions' => $conditions
+ );
+ $events = $this->Paginator->paginate('Event');
+
+ // For each event, get its thumbnail data (path, width, height)
+ foreach ($events as $key => $value) {
+ $thumbData = $this->createThumbnail($value['Event']['Id']);
+ $events[$key]['thumbData'] = $thumbData;
+
+ }
+
+ $this->set(compact('events'));
}
/**
@@ -36,12 +62,23 @@ class EventsController extends AppController {
* @return void
*/
public function view($id = null) {
- $this->Event->recursive = -1;
+ $this->loadModel('Config');
+ $configs = $this->Config->find('list', array(
+ 'fields' => array('Name', 'Value'),
+ 'conditions' => array('Name' => array('ZM_DIR_EVENTS'))
+ ));
+
+ $this->Event->recursive = 1;
if (!$this->Event->exists($id)) {
throw new NotFoundException(__('Invalid event'));
}
$options = array('conditions' => array('Event.' . $this->Event->primaryKey => $id));
$event = $this->Event->find('first', $options);
+
+ $path = $configs['ZM_DIR_EVENTS'].'/'.$this->Image->getEventPath($event).'/';
+
+ $event['Event']['BasePath'] = $path;
+
$this->set(array(
'event' => $event,
'_serialize' => array('event')
@@ -108,4 +145,142 @@ class EventsController extends AppController {
} else {
return $this->flash(__('The event could not be deleted. Please, try again.'), array('action' => 'index'));
}
- }}
+ }
+
+ public function search() {
+ $this->Event->recursive = -1;
+ $conditions = array();
+
+ foreach ($this->params['named'] as $param_name => $value) {
+ // Transform params into mysql
+ if (preg_match("/interval/i", $value, $matches)) {
+ $condition = array("$param_name >= (date_sub(now(), $value))");
+ } else {
+ $condition = array($param_name => $value);
+ }
+ array_push($conditions, $condition);
+ }
+
+ $results = $this->Event->find('all', array(
+ 'conditions' => $conditions
+ ));
+
+ $this->set(array(
+ 'results' => $results,
+ '_serialize' => array('results')
+ ));
+
+
+ }
+
+ public function consoleEvents($interval = null) {
+ $this->Event->recursive = -1;
+ $results = array();
+
+ $query = $this->Event->query("select MonitorId, COUNT(*) AS Count from Events WHERE StartTime >= (DATE_SUB(NOW(), interval $interval)) GROUP BY MonitorId;");
+
+ foreach ($query as $result) {
+ $results[$result['Events']['MonitorId']] = $result[0]['Count'];
+ }
+
+ $this->set(array(
+ 'results' => $results,
+ '_serialize' => array('results')
+ ));
+ }
+
+ // Create a thumbnail and return the thumbnail's data for a given event id.
+ public function createThumbnail($id = null) {
+ $this->Event->recursive = -1;
+
+ if (!$this->Event->exists($id)) {
+ throw new NotFoundException(__('Invalid event'));
+ }
+
+ $event = $this->Event->find('first', array(
+ 'conditions' => array('Id' => $id)
+ ));
+
+ // Find the max Frame for this Event. Error out otherwise.
+ $this->loadModel('Frame');
+ if (! $frame = $this->Frame->find('first', array(
+ 'conditions' => array(
+ 'EventId' => $event['Event']['Id'],
+ 'Score' => $event['Event']['MaxScore']
+ )
+ ))) {
+ throw new NotFoundException(__("Can not find Frame for Event " . $event['Event']['Id']));
+ }
+
+ $this->loadModel('Config');
+
+ // Get the config options required for reScale and getImageSrc
+ // The $bw, $thumbs and unset() code is a workaround / temporary
+ // until I have a better way of handing per-bandwidth config options
+ $bw = (isset($_COOKIE['zmBandwidth']) ? strtoupper(substr($_COOKIE['zmBandwidth'], 0, 1)) : 'L');
+ $thumbs = "ZM_WEB_${bw}_SCALE_THUMBS";
+
+ $config = $this->Config->find('list', array(
+ 'conditions' => array('OR' => array(
+ 'Name' => array('ZM_WEB_LIST_THUMB_WIDTH',
+ 'ZM_WEB_LIST_THUMB_HEIGHT',
+ 'ZM_EVENT_IMAGE_DIGITS',
+ 'ZM_DIR_IMAGES',
+ "$thumbs",
+ 'ZM_DIR_EVENTS'
+ )
+ )),
+ 'fields' => array('Name', 'Value')
+ ));
+ $config['ZM_WEB_SCALE_THUMBS'] = $config[$thumbs];
+ unset($config[$thumbs]);
+
+ // reScale based on either the width, or the hight, of the event.
+ if ( $config['ZM_WEB_LIST_THUMB_WIDTH'] ) {
+ $thumbWidth = $config['ZM_WEB_LIST_THUMB_WIDTH'];
+ $scale = (100 * $thumbWidth) / $event['Event']['Width'];
+ $thumbHeight = $this->Scaler->reScale( $event['Event']['Height'], $scale );
+ }
+ elseif ( $config['ZM_WEB_LIST_THUMB_HEIGHT'] ) {
+ $thumbHeight = $config['ZM_WEB_LIST_THUMB_HEIGHT'];
+ $scale = (100*$thumbHeight)/$event['Event']['Height'];
+ $thumbWidth = $this->Scaler->reScale( $event['Event']['Width'], $scale );
+ }
+ else {
+ throw new NotFoundException(__('No thumbnail width or height specified, please check in Options->Web'));
+ }
+
+ $imageData = $this->Image->getImageSrc( $event, $frame, $scale, $config );
+ $thumbData['Path'] = $imageData['thumbPath'];
+ $thumbData['Width'] = (int)$thumbWidth;
+ $thumbData['Height'] = (int)$thumbHeight;
+
+ return( $thumbData );
+
+ }
+
+ public function archive($id = null) {
+ $this->Event->recursive = -1;
+ if (!$this->Event->exists($id)) {
+ throw new NotFoundException(__('Invalid event'));
+ }
+
+ // Get the current value of Archive
+ $archived = $this->Event->find('first', array(
+ 'fields' => array('Event.Archived'),
+ 'conditions' => array('Event.Id' => $id)
+ ));
+ // If 0, 1, if 1, 0
+ $archiveVal = (($archived['Event']['Archived'] == 0) ? 1 : 0);
+
+ // Save the new value
+ $this->Event->id = $id;
+ $this->Event->saveField('Archived', $archiveVal);
+
+ $this->set(array(
+ 'archived' => $archiveVal,
+ '_serialize' => array('archived')
+ ));
+ }
+
+}
diff --git a/web/api/app/Controller/HostController.php b/web/api/app/Controller/HostController.php
new file mode 100644
index 000000000..973caf286
--- /dev/null
+++ b/web/api/app/Controller/HostController.php
@@ -0,0 +1,110 @@
+set(array(
+ 'result' => $result,
+ '_serialize' => array('result')
+ ));
+ }
+
+ function getLoad() {
+ $load = sys_getloadavg();
+
+ $this->set(array(
+ 'load' => $load,
+ '_serialize' => array('load')
+ ));
+ }
+
+ // If $mid is set, only return disk usage for that monitor
+ // Else, return an array of total disk usage, and per-monitor
+ // usage.
+ function getDiskPercent($mid = null) {
+ $this->loadModel('Config');
+ $this->loadModel('Monitor');
+
+ // If $mid is passed, see if it is valid
+ if ($mid) {
+ if (!$this->Monitor->exists($mid)) {
+ throw new NotFoundException(__('Invalid monitor'));
+ }
+ }
+
+ $zm_dir_events = $this->Config->find('list', array(
+ 'conditions' => array('Name' => 'ZM_DIR_EVENTS'),
+ 'fields' => array('Name', 'Value')
+ ));
+ $zm_dir_events = $zm_dir_events['ZM_DIR_EVENTS' ];
+
+ // Test to see if $zm_dir_events is relative or absolute
+ if ('/' === "" || strrpos($zm_dir_events, '/', -strlen($zm_dir_events)) !== TRUE) {
+ // relative - so add the full path
+ $zm_dir_events = Configure::read('ZM_PATH_WEB') . '/' . $zm_dir_events;
+ }
+
+ if ($mid) {
+ // Get disk usage for $mid
+ $usage = shell_exec ("du -sh0 $zm_dir_events/$mid | awk '{print $1}'");
+ } else {
+ $monitors = $this->Monitor->find('all', array(
+ 'fields' => array('Id', 'Name', 'WebColour')
+ ));
+ $usage = array();
+
+ // Add each monitor's usage to array
+ foreach ($monitors as $key => $value) {
+ $id = $value['Monitor']['Id'];
+ $name = $value['Monitor']['Name'];
+ $color = $value['Monitor']['WebColour'];
+
+ $space = shell_exec ("du -s0 $zm_dir_events/$id | awk '{print $1}'");
+ if ($space == null) {
+ $space = 0;
+ }
+ $space = $space/1024/1024;
+
+ $usage[$name] = array(
+ 'space' => rtrim($space),
+ 'color' => $color
+ );
+ }
+
+ // Add total usage to array
+ $space = shell_exec( "df $zm_dir_events |tail -n1 | awk '{print $3 }'");
+ $space = $space/1024/1024;
+ $usage['Total'] = array(
+ 'space' => rtrim($space),
+ 'color' => '#F7464A'
+ );
+ }
+
+ $this->set(array(
+ 'usage' => $usage,
+ '_serialize' => array('usage')
+ ));
+ }
+
+ function getVersion() {
+ $version = Configure::read('ZM_VERSION');
+
+ $this->set(array(
+ 'version' => $version,
+ '_serialize' => array('version')
+ ));
+ }
+}
diff --git a/web/api/app/Controller/LogsController.php b/web/api/app/Controller/LogsController.php
index 908c635e2..bb8f47b80 100644
--- a/web/api/app/Controller/LogsController.php
+++ b/web/api/app/Controller/LogsController.php
@@ -13,7 +13,12 @@ class LogsController extends AppController {
*
* @var array
*/
- public $components = array('Paginator');
+ public $components = array('Paginator', 'RequestHandler');
+ public $paginate = array(
+ 'limit' => 100,
+ 'order' => array( 'Log.TimeKey' => 'asc' ),
+ 'paramType' => 'querystring'
+ );
/**
* index method
@@ -21,8 +26,11 @@ class LogsController extends AppController {
* @return void
*/
public function index() {
- $this->Log->recursive = 0;
- $this->set('logs', $this->Paginator->paginate());
+ $this->Log->recursive = -1;
+ $this->Paginator->settings = $this->paginate;
+
+ $logs = $this->Paginator->paginate('Log');
+ $this->set(compact('logs'));
}
/**
diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php
index da4339e8e..0588a8cc2 100644
--- a/web/api/app/Controller/MonitorsController.php
+++ b/web/api/app/Controller/MonitorsController.php
@@ -59,6 +59,7 @@ class MonitorsController extends AppController {
if ($this->request->is('post')) {
$this->Monitor->create();
if ($this->Monitor->save($this->request->data)) {
+ $this->daemonControl($this->Monitor->id, 'start', $this->request->data);
return $this->flash(__('The monitor has been saved.'), array('action' => 'index'));
}
}
@@ -103,6 +104,9 @@ class MonitorsController extends AppController {
throw new NotFoundException(__('Invalid monitor'));
}
$this->request->allowMethod('post', 'delete');
+
+ $this->daemonControl($this->Monitor->id, 'stop');
+
if ($this->Monitor->delete()) {
return $this->flash(__('The monitor has been deleted.'), array('action' => 'index'));
} else {
@@ -124,5 +128,81 @@ class MonitorsController extends AppController {
));
}
+ // Check if a daemon is running for the monitor id
+ public function daemonStatus() {
+ $id = $this->request->params['named']['id'];
+ $daemon = $this->request->params['named']['daemon'];
+
+ if (!$this->Monitor->exists($id)) {
+ throw new NotFoundException(__('Invalid monitor'));
+ }
+
+ $monitor = $this->Monitor->find('first', array(
+ 'fields' => array('Id', 'Type', 'Device'),
+ 'conditions' => array('Id' => $id)
+ ));
+
+ // Clean up the returned array
+ $monitor = Set::extract('/Monitor/.', $monitor);
+
+ // Pass -d for local, otherwise -m
+ if ($monitor[0]['Type'] == 'Local') {
+ $args = "-d ". $monitor[0]['Device'];
+ } else {
+ $args = "-m ". $monitor[0]['Id'];
+ }
+
+ // Build the command, and execute it
+ $zm_path_bin = Configure::read('ZM_PATH_BIN');
+ $command = escapeshellcmd("$zm_path_bin/zmdc.pl status $daemon $args");
+ $status = exec( $command );
+
+ // If 'not' is present, the daemon is not running, so return false
+ // https://github.com/ZoneMinder/ZoneMinder/issues/799#issuecomment-108996075
+ // Also sending back the status text so we can check if the monitor is in pending
+ // state which means there may be an error
+ $statustext = $status;
+ $status = (strpos($status, 'not')) ? false : true;
+
+ $this->set(array(
+ 'status' => $status,
+ 'statustext' => $statustext,
+ '_serialize' => array('status','statustext'),
+ ));
+ }
+
+ public function daemonControl($id, $command, $monitor=null, $daemon=null) {
+ $args = '';
+ $daemons = array();
+
+ if (!$monitor) {
+ // Need to see if it is local or remote
+ $monitor = $this->Monitor->find('first', array(
+ 'fields' => array('Type', 'Function'),
+ 'conditions' => array('Id' => $id)
+ ));
+ $monitor = $monitor['Monitor'];
+ }
+
+ if ($monitor['Type'] == 'Local') {
+ $args = "-d " . $monitor['Device'];
+ } else {
+ $args = "-m " . $id;
+ }
+
+ if ($monitor['Function'] == 'Monitor') {
+ array_push($daemons, 'zmc');
+ } else {
+ array_push($daemons, 'zmc', 'zma');
+ }
+
+ $zm_path_bin = Configure::read('ZM_PATH_BIN');
+
+ foreach ($daemons as $daemon) {
+ $shellcmd = escapeshellcmd("$zm_path_bin/zmdc.pl $command $daemon $args");
+ $status = exec( $shellcmd );
+ }
+ }
+
}
diff --git a/web/api/app/Controller/StatesController.php b/web/api/app/Controller/StatesController.php
new file mode 100644
index 000000000..2b007f08f
--- /dev/null
+++ b/web/api/app/Controller/StatesController.php
@@ -0,0 +1,117 @@
+State->recursive = 0;
+ $states = $this->State->find('all');
+ $this->set(array(
+ 'states' => $states,
+ '_serialize' => array('states')
+ ));
+ }
+
+/**
+ * view method
+ *
+ * @throws NotFoundException
+ * @param string $id
+ * @return void
+ */
+ public function view($id = null) {
+ if (!$this->State->exists($id)) {
+ throw new NotFoundException(__('Invalid state'));
+ }
+ $options = array('conditions' => array('State.' . $this->State->primaryKey => $id));
+ $this->set('state', $this->State->find('first', $options));
+ }
+
+/**
+ * add method
+ *
+ * @return void
+ */
+ public function add() {
+ if ($this->request->is('post')) {
+ $this->State->create();
+ if ($this->State->save($this->request->data)) {
+ return $this->flash(__('The state has been saved.'), array('action' => 'index'));
+ }
+ }
+ }
+
+/**
+ * edit method
+ *
+ * @throws NotFoundException
+ * @param string $id
+ * @return void
+ */
+ public function edit($id = null) {
+ if (!$this->State->exists($id)) {
+ throw new NotFoundException(__('Invalid state'));
+ }
+ if ($this->request->is(array('post', 'put'))) {
+ if ($this->State->save($this->request->data)) {
+ return $this->flash(__('The state has been saved.'), array('action' => 'index'));
+ }
+ } else {
+ $options = array('conditions' => array('State.' . $this->State->primaryKey => $id));
+ $this->request->data = $this->State->find('first', $options);
+ }
+ }
+
+/**
+ * delete method
+ *
+ * @throws NotFoundException
+ * @param string $id
+ * @return void
+ */
+ public function delete($id = null) {
+ $this->State->id = $id;
+ if (!$this->State->exists()) {
+ throw new NotFoundException(__('Invalid state'));
+ }
+ $this->request->allowMethod('post', 'delete');
+ if ($this->State->delete()) {
+ return $this->flash(__('The state has been deleted.'), array('action' => 'index'));
+ } else {
+ return $this->flash(__('The state could not be deleted. Please, try again.'), array('action' => 'index'));
+ }
+ }
+
+ public function change() {
+ $newState = $this->request->params['pass'][0];
+ $blah = $this->packageControl($newState);
+
+ $this->set(array(
+ 'blah' => $blah,
+ '_serialize' => array('blah')
+ ));
+ }
+
+ public function packageControl( $command ) {
+ $zm_path_bin = Configure::read('ZM_PATH_BIN');
+ $string = $zm_path_bin.'/zmpkg.pl '.escapeshellarg( $command );
+ $status = exec( $string );
+
+ return $status;
+ }
+
+
+}
diff --git a/web/api/app/Controller/ZonePresetsController.php b/web/api/app/Controller/ZonePresetsController.php
new file mode 100644
index 000000000..b89a6c75d
--- /dev/null
+++ b/web/api/app/Controller/ZonePresetsController.php
@@ -0,0 +1,99 @@
+ZonePreset->find('all');
+ $this->set(array(
+ 'zonePresets' => $zonePresets,
+ '_serialize' => array('zonePresets')
+ ));
+ }
+
+/**
+ * view method
+ *
+ * @throws NotFoundException
+ * @param string $id
+ * @return void
+ */
+ public function view($id = null) {
+ if (!$this->ZonePreset->exists($id)) {
+ throw new NotFoundException(__('Invalid zone preset'));
+ }
+ $options = array('conditions' => array('ZonePreset.' . $this->ZonePreset->primaryKey => $id));
+ $this->set('zonePreset', $this->ZonePreset->find('first', $options));
+ }
+
+/**
+ * add method
+ *
+ * @return void
+ */
+ public function add() {
+ if ($this->request->is('post')) {
+ $this->ZonePreset->create();
+ if ($this->ZonePreset->save($this->request->data)) {
+ return $this->flash(__('The zone preset has been saved.'), array('action' => 'index'));
+ }
+ }
+ }
+
+/**
+ * edit method
+ *
+ * @throws NotFoundException
+ * @param string $id
+ * @return void
+ */
+ public function edit($id = null) {
+ if (!$this->ZonePreset->exists($id)) {
+ throw new NotFoundException(__('Invalid zone preset'));
+ }
+ if ($this->request->is(array('post', 'put'))) {
+ if ($this->ZonePreset->save($this->request->data)) {
+ return $this->flash(__('The zone preset has been saved.'), array('action' => 'index'));
+ }
+ } else {
+ $options = array('conditions' => array('ZonePreset.' . $this->ZonePreset->primaryKey => $id));
+ $this->request->data = $this->ZonePreset->find('first', $options);
+ }
+ }
+
+/**
+ * delete method
+ *
+ * @throws NotFoundException
+ * @param string $id
+ * @return void
+ */
+ public function delete($id = null) {
+ $this->ZonePreset->id = $id;
+ if (!$this->ZonePreset->exists()) {
+ throw new NotFoundException(__('Invalid zone preset'));
+ }
+ $this->request->allowMethod('post', 'delete');
+ if ($this->ZonePreset->delete()) {
+ return $this->flash(__('The zone preset has been deleted.'), array('action' => 'index'));
+ } else {
+ return $this->flash(__('The zone preset could not be deleted. Please, try again.'), array('action' => 'index'));
+ }
+ }}
diff --git a/web/api/app/Controller/ZonesController.php b/web/api/app/Controller/ZonesController.php
index dc30a382a..be80c57fc 100644
--- a/web/api/app/Controller/ZonesController.php
+++ b/web/api/app/Controller/ZonesController.php
@@ -4,51 +4,26 @@ App::uses('AppController', 'Controller');
* Zones Controller
*
* @property Zone $Zone
- * @property PaginatorComponent $Paginator
*/
class ZonesController extends AppController {
-/**
- * Components
- *
- * @var array
- */
- public $components = array('Paginator', 'RequestHandler');
-
-/**
- * index method
- *
- * @return void
- */
- public function index() {
- $this->Zone->recursive = -1;
- $zones = $this->Zone->find('all');
- $this->set(array(
- 'zones' => $zones,
- '_serialize' => array('zones')
- ));
- }
-
-/**
- * view method
- *
- * @throws NotFoundException
- * @param string $id
- * @return void
- */
- public function view($id = null) {
- $this->Zone->recursive = -1;
- if (!$this->Zone->exists($id)) {
- throw new NotFoundException(__('Invalid zone'));
+// Find all zones which belong to a MonitorId
+ public function forMonitor($id = null) {
+ $this->loadModel('Monitor');
+ if (!$this->Monitor->exists($id)) {
+ throw new NotFoundException(__('Invalid monitor'));
}
- $options = array('conditions' => array('Zone.' . $this->Zone->primaryKey => $id));
- $zone = $this->Zone->find('first', $options);
+
+ $this->Zone->recursive = -1;
+
+ $zones = $this->Zone->find('all', array(
+ 'conditions' => array('MonitorId' => $id)
+ ));
$this->set(array(
- 'zone' => $zone,
- '_serialize' => array('zone')
+ 'zones' => $zones,
+ '_serialize' => array('zones')
));
}
-
/**
* add method
*
@@ -108,4 +83,38 @@ class ZonesController extends AppController {
} else {
return $this->flash(__('The zone could not be deleted. Please, try again.'), array('action' => 'index'));
}
- }}
+ }
+
+
+
+ public function createZoneImage( $id = null ) {
+ $this->loadModel('Monitor');
+ $this->Monitor->id = $id;
+ if (!$this->Monitor->exists()) {
+ throw new NotFoundException(__('Invalid zone'));
+ }
+
+
+ $this->loadModel('Config');
+ $zm_dir_images = $this->Config->find('list', array(
+ 'conditions' => array('Name' => 'ZM_DIR_IMAGES'),
+ 'fields' => array('Name', 'Value')
+ ));
+
+ $zm_dir_images = $zm_dir_images['ZM_DIR_IMAGES'];
+ $zm_path_web = Configure::read('ZM_PATH_WEB');
+ $zm_path_bin = Configure::read('ZM_PATH_BIN');
+ $images_path = "$zm_path_web/$zm_dir_images";
+
+ chdir($images_path);
+
+ $command = escapeshellcmd("$zm_path_bin/zmu -z -m $id");
+ system( $command, $status );
+
+ $this->set(array(
+ 'status' => $status,
+ '_serialize' => array('status')
+ ));
+
+ }
+}
diff --git a/web/api/app/Model/Config.php b/web/api/app/Model/Config.php
index 2ada8258d..d83728c76 100644
--- a/web/api/app/Model/Config.php
+++ b/web/api/app/Model/Config.php
@@ -18,13 +18,25 @@ class Config extends AppModel {
*
* @var string
*/
- public $primaryKey = 'Id';
+ public $primaryKey = 'Name';
/**
* Display field
*
* @var string
*/
- public $displayField = 'Name';
+ public $displayField = 'Value';
+
+
+ // Add a find method for returning a hash of the Config table.
+ // This is used for the Options view.
+ public $findMethods = array('hash' => true);
+ protected function _findHash($state, $query, $results = array()) {
+ if ($state === 'before') {
+ return $query;
+ }
+ $results = Set::combine($results, '{n}.Config.Name', '{n}.Config');
+ return $results;
+ }
}
diff --git a/web/api/app/Model/Control.php b/web/api/app/Model/Control.php
new file mode 100644
index 000000000..d5716cc66
--- /dev/null
+++ b/web/api/app/Model/Control.php
@@ -0,0 +1,54 @@
+ array(
+ 'numeric' => array(
+ 'rule' => array('numeric'),
+ //'message' => 'Your custom message here',
+ //'allowEmpty' => false,
+ //'required' => false,
+ //'last' => false, // Stop validation after this rule
+ //'on' => 'create', // Limit validation to 'create' or 'update' operations
+ ),
+ ),
+ );
+
+}
diff --git a/web/api/app/Model/Frame.php b/web/api/app/Model/Frame.php
index 48b880304..96c5fdc50 100644
--- a/web/api/app/Model/Frame.php
+++ b/web/api/app/Model/Frame.php
@@ -121,4 +121,6 @@ class Frame extends AppModel {
'order' => ''
)
);
+
+ public $recursive = -1;
}
diff --git a/web/api/app/Model/Host.php b/web/api/app/Model/Host.php
new file mode 100644
index 000000000..5c24c7531
--- /dev/null
+++ b/web/api/app/Model/Host.php
@@ -0,0 +1,9 @@
+
+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('No controllers found to be processed ');
+ 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[] = '_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("Skipping argument: $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("Skipping: $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("Skipping: $className, doesn't use any models", 1, Shell::VERBOSE);
+ return;
+ }
+
+ if (!isset($Controller->Crud)) {
+ $this->out("Skipping: $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('Skipping: ' . $text, 1, Shell::VERBOSE);
+ } else {
+ $this->out('Adding: ' . $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;
+ }
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Component/CrudComponent.php b/web/api/app/Plugin/Crud/Controller/Component/CrudComponent.php
new file mode 100644
index 000000000..5a49fd091
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Component/CrudComponent.php
@@ -0,0 +1,845 @@
+ $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;
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Action/AddCrudAction.php b/web/api/app/Plugin/Crud/Controller/Crud/Action/AddCrudAction.php
new file mode 100644
index 000000000..3163dc29e
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/Action/AddCrudAction.php
@@ -0,0 +1,131 @@
+ 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();
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Action/DeleteCrudAction.php b/web/api/app/Plugin/Crud/Controller/Crud/Action/DeleteCrudAction.php
new file mode 100644
index 000000000..e5f0184ed
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/Action/DeleteCrudAction.php
@@ -0,0 +1,108 @@
+ 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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Action/EditCrudAction.php b/web/api/app/Plugin/Crud/Controller/Crud/Action/EditCrudAction.php
new file mode 100644
index 000000000..1d5efadfe
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/Action/EditCrudAction.php
@@ -0,0 +1,293 @@
+ 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']);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Action/IndexCrudAction.php b/web/api/app/Plugin/Crud/Controller/Crud/Action/IndexCrudAction.php
new file mode 100644
index 000000000..b93cb2b67
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/Action/IndexCrudAction.php
@@ -0,0 +1,120 @@
+ 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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Action/ViewCrudAction.php b/web/api/app/Plugin/Crud/Controller/Crud/Action/ViewCrudAction.php
new file mode 100644
index 000000000..6248fea29
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/Action/ViewCrudAction.php
@@ -0,0 +1,94 @@
+ 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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/CrudAction.php b/web/api/app/Plugin/Crud/Controller/Crud/CrudAction.php
new file mode 100644
index 000000000..c35717b9e
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/CrudAction.php
@@ -0,0 +1,441 @@
+_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;
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/CrudBaseObject.php b/web/api/app/Plugin/Crud/Controller/Crud/CrudBaseObject.php
new file mode 100644
index 000000000..d20b1dee8
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/CrudBaseObject.php
@@ -0,0 +1,277 @@
+_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;
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/CrudListener.php b/web/api/app/Plugin/Crud/Controller/Crud/CrudListener.php
new file mode 100644
index 000000000..3a82b7e9c
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/CrudListener.php
@@ -0,0 +1,67 @@
+ '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;
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/CrudSubject.php b/web/api/app/Plugin/Crud/Controller/Crud/CrudSubject.php
new file mode 100644
index 000000000..1c4309c35
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/CrudSubject.php
@@ -0,0 +1,157 @@
+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');
+ }
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiFieldFilterListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiFieldFilterListener.php
new file mode 100644
index 000000000..2b6bf8e88
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiFieldFilterListener.php
@@ -0,0 +1,348 @@
+_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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiListener.php
new file mode 100644
index 000000000..5fc25ca2b
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiListener.php
@@ -0,0 +1,443 @@
+ 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);
+ }
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiPaginationListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiPaginationListener.php
new file mode 100644
index 000000000..263eaa8d0
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiPaginationListener.php
@@ -0,0 +1,59 @@
+ 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);
+ }
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiQueryLogListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiQueryLogListener.php
new file mode 100644
index 000000000..9a575facf
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiQueryLogListener.php
@@ -0,0 +1,100 @@
+ 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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiTransformationListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiTransformationListener.php
new file mode 100644
index 000000000..99605bcb4
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiTransformationListener.php
@@ -0,0 +1,325 @@
+ 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);
+ }
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/DebugKitListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/DebugKitListener.php
new file mode 100644
index 000000000..778892c4d
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/Listener/DebugKitListener.php
@@ -0,0 +1,157 @@
+ 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');
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/RedirectListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/RedirectListener.php
new file mode 100644
index 000000000..c3746f197
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/Listener/RedirectListener.php
@@ -0,0 +1,176 @@
+ 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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/RelatedModelsListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/RelatedModelsListener.php
new file mode 100644
index 000000000..7e5313541
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/Listener/RelatedModelsListener.php
@@ -0,0 +1,210 @@
+_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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ScaffoldListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ScaffoldListener.php
new file mode 100644
index 000000000..559bc67b5
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ScaffoldListener.php
@@ -0,0 +1,159 @@
+ '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;
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/SearchListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/SearchListener.php
new file mode 100644
index 000000000..54e96bfa7
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Controller/Crud/Listener/SearchListener.php
@@ -0,0 +1,211 @@
+ 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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Error/CrudExceptionRenderer.php b/web/api/app/Plugin/Crud/Error/CrudExceptionRenderer.php
new file mode 100644
index 000000000..8c84655cb
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Error/CrudExceptionRenderer.php
@@ -0,0 +1,132 @@
+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;
+ }
+}
diff --git a/web/api/app/Plugin/Crud/Error/Exception/CrudValidationException.php b/web/api/app/Plugin/Crud/Error/Exception/CrudValidationException.php
new file mode 100644
index 000000000..368c311b2
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Error/Exception/CrudValidationException.php
@@ -0,0 +1,98 @@
+_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;
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/LICENSE.txt b/web/api/app/Plugin/Crud/LICENSE.txt
new file mode 100644
index 000000000..bbf75212a
--- /dev/null
+++ b/web/api/app/Plugin/Crud/LICENSE.txt
@@ -0,0 +1,21 @@
+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.
diff --git a/web/api/app/Plugin/Crud/Lib/CrudControllerTrait.php b/web/api/app/Plugin/Crud/Lib/CrudControllerTrait.php
new file mode 100644
index 000000000..9921e5fc1
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Lib/CrudControllerTrait.php
@@ -0,0 +1,64 @@
+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;
+ }
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Lib/Panel/CrudPanel.php b/web/api/app/Plugin/Crud/Lib/Panel/CrudPanel.php
new file mode 100644
index 000000000..4821ffaa5
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Lib/Panel/CrudPanel.php
@@ -0,0 +1,125 @@
+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;
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/README.md b/web/api/app/Plugin/Crud/README.md
new file mode 100644
index 000000000..c99173964
--- /dev/null
+++ b/web/api/app/Plugin/Crud/README.md
@@ -0,0 +1,59 @@
+[](https://waffle.io/friendsofcake/crud)
+[](https://travis-ci.org/FriendsOfCake/crud)
+[](https://coveralls.io/r/FriendsOfCake/crud?branch=master)
+[](https://packagist.org/packages/FriendsOfCake/crud)
+[](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.
diff --git a/web/api/app/Plugin/Crud/Routing/Filter/FakeHeadFilter.php b/web/api/app/Plugin/Crud/Routing/Filter/FakeHeadFilter.php
new file mode 100644
index 000000000..25cba5c70
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Routing/Filter/FakeHeadFilter.php
@@ -0,0 +1,60 @@
+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';
+ }
+ }
+}
diff --git a/web/api/app/Plugin/Crud/Routing/Filter/HttpMethodFilter.php b/web/api/app/Plugin/Crud/Routing/Filter/HttpMethodFilter.php
new file mode 100644
index 000000000..c7061361c
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Routing/Filter/HttpMethodFilter.php
@@ -0,0 +1,104 @@
+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;
+ }
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/AllCrudTest.php b/web/api/app/Plugin/Crud/Test/Case/AllCrudTest.php
new file mode 100644
index 000000000..4f09ca87f
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/AllCrudTest.php
@@ -0,0 +1,34 @@
+addTestDirectory($folder);
+ }
+
+ $suite->addTestDirectory($testPath);
+ return $suite;
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Console/Command/TranslationsShellTest.php b/web/api/app/Plugin/Crud/Test/Case/Console/Command/TranslationsShellTest.php
new file mode 100644
index 000000000..ee34ce3f1
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Console/Command/TranslationsShellTest.php
@@ -0,0 +1,449 @@
+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 = <<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 = <<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);
+ }
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Component/CrudComponentTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Component/CrudComponentTest.php
new file mode 100644
index 000000000..01763efff
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Component/CrudComponentTest.php
@@ -0,0 +1,1174 @@
+_log[] = array(
+ 'name' => $event->name(),
+ 'subject' => $event->subject()
+ );
+ parent::dispatch($event);
+ }
+
+ public function getLog($params = array()) {
+ $params += array('clear' => true, 'format' => 'names');
+
+ $log = $this->_log;
+
+ if ($params['format'] === 'names') {
+ $return = array();
+ foreach ($log as $entry) {
+ $return[] = $entry['name'];
+ }
+ $log = $return;
+ }
+
+ if ($params['clear']) {
+ $this->_log = array();
+ }
+
+ return $log;
+ }
+
+}
+
+class CrudExamplesController extends Controller {
+
+ public $uses = array('CrudExample');
+
+ public $modelClass = 'CrudExample';
+
+ public static $componentsArray = array(
+ 'Session',
+ 'Crud.Crud' => array(
+ 'actions' => array(
+ 'index',
+ 'add',
+ 'edit',
+ 'delete',
+ 'view'
+ )
+ )
+ );
+
+ public $paginate = array(
+ 'limit' => 1000
+ );
+
+/**
+ * Make it possible to dynamically define the components array during tests
+ *
+ * @param CakeRequest $request
+ * @param CakeResponse $response
+ * @return void
+ */
+ public function __construct($request = null, $response = null) {
+ $this->components = self::$componentsArray;
+
+ return parent::__construct($request, $response);
+ }
+
+/**
+ * add
+ *
+ * Used in the testAddActionTranslatedBaseline test
+ *
+ * @return void
+ */
+ public function add() {
+ return $this->Crud->execute();
+ }
+
+/**
+ * Test that it should render 'search.ctp'
+ *
+ * @return void
+ */
+ public function search() {
+ return $this->Crud->execute('index');
+ }
+
+/**
+ * Test that it should render 'index'
+ *
+ * @return void
+ */
+ public function index() {
+ return $this->Crud->execute('index');
+ }
+
+}
+
+/**
+ * TestCrudComponent
+ *
+ * Expose protected methods so we can test them in isolation
+ */
+class TestCrudComponent extends CrudComponent {
+
+/**
+ * test visibility wrapper - access protected _modelName property
+ */
+ public function getModelName() {
+ return $this->_modelName;
+ }
+
+/**
+ * test visibility wrapper - call protected method _setModelProperties
+ */
+ public function setModelProperties() {
+ return parent::_setModelProperties();
+ }
+
+/**
+ * test visibility wrapper - allow on the fly change of action name
+ */
+ public function setAction($name) {
+ $this->_action = $name;
+ }
+
+}
+
+class TestListener extends CrudListener {
+
+ public $callCount = 0;
+
+ public function setup() {
+ $this->callCount += 1;
+ }
+
+}
+
+/**
+ * CrudComponentTestCase
+ */
+class CrudComponentTest extends CrudControllerTestCase {
+
+/**
+ * fixtures
+ *
+ * Use the core posts fixture to have something to work on.
+ * What fixture is used is almost irrelevant, was chosen as it is simple
+ */
+ public $fixtures = array(
+ 'core.post', 'core.author', 'core.tag', 'core.comment', 'core.flag_tree',
+ 'plugin.crud.posts_tag', 'core.cake_session'
+ );
+
+/**
+ * setUp
+ *
+ * Setup the classes the crud component needs to be testable
+ */
+ public function setUp() {
+ require_once ('models.php');
+
+ parent::setUp();
+
+ CakeEventManager::instance(new TestCrudEventManager());
+
+ ConnectionManager::getDataSource('test')->getLog();
+
+ $this->model = new CrudExample();
+
+ $this->controller = $this->getMock(
+ 'CrudExamplesController',
+ array('header', 'redirect', 'render', '_stop'),
+ array(),
+ '',
+ false
+ );
+ $this->controller->name = 'CrudExamples';
+
+ $this->request = $this->getMock('CakeRequest', array('is', 'method'));
+ $this->request->expects($this->any())->method('is')->will($this->returnValue(true));
+
+ $response = new CakeResponse();
+ $this->controller->__construct($this->request, $response);
+ $this->controller->methods = array();
+
+ $this->Collection = new ComponentCollection();
+ $this->Collection->init($this->controller);
+ $this->controller->Components = $this->Collection;
+
+ $settings = array(
+ 'actions' => array(
+ 'index',
+ 'add',
+ 'edit',
+ 'view',
+ 'delete'
+ )
+ );
+
+ $this->Crud = new TestCrudComponent($this->Collection, $settings);
+ $this->Crud->initialize($this->controller);
+ $this->controller->Crud = $this->Crud;
+ }
+
+/**
+ * tearDown method
+ */
+ public function tearDown() {
+ unset(
+ $this->model,
+ $this->request,
+ $this->controller,
+ $this->Crud,
+ $this->Collection
+ );
+
+ parent::tearDown();
+ }
+
+/**
+ * Test config normalization
+ *
+ * @return void
+ */
+ public function testConfigNormalization() {
+ $Collection = $this->getMock('ComponentCollection');
+
+ $settings = array(
+ 'actions' => array(
+ 'index',
+ 'admin_index',
+ 'add' => 'Crud.Add',
+ 'view' => array('viewVar' => 'beers'),
+ 'edit' => array('viewVar' => 'beers', 'className' => 'MyPlugin.MyEdit'),
+ 'foo' => 'index'
+ ),
+ 'listeners' => array(
+ 'Related' => 'Related',
+ 'Mylistener' => 'MyPlugin.Mylistener'
+ )
+ );
+ $Crud = $this->getMock('CrudComponent', array('_loadListeners', 'trigger'), array($Collection, $settings));
+ $Crud
+ ->expects($this->once())
+ ->method('_loadListeners');
+ $Crud
+ ->expects($this->once())
+ ->method('trigger');
+ $Crud->initialize($this->controller);
+
+ $expected = array(
+ 'index' => array('className' => 'Crud.Index'),
+ 'admin_index' => array('className' => 'Crud.Index'),
+ 'add' => array('className' => 'Crud.Add'),
+ 'view' => array('viewVar' => 'beers', 'className' => 'Crud.View'),
+ 'edit' => array('viewVar' => 'beers', 'className' => 'MyPlugin.MyEdit'),
+ 'foo' => array('className' => 'Crud.Index')
+ );
+ $this->assertEquals($expected, $Crud->settings['actions']);
+
+ $expected = array(
+ 'Related' => array('className' => 'Crud.Related'),
+ 'Mylistener' => array('className' => 'MyPlugin.Mylistener'),
+ 'RelatedModels' => array('className' => 'Crud.RelatedModels'),
+ );
+ $this->assertEquals($expected, $Crud->settings['listeners']);
+ }
+
+/**
+ * Test deprecated `executeAction` calls `execute` correctly
+ *
+ */
+ public function testExecuteActionToExecute() {
+ $Collection = $this->getMock('ComponentCollection');
+ $settings = array('actions' => array('index'));
+
+ $Crud = $this->getMock('CrudComponent', array('execute'), array($Collection, $settings));
+ $Crud
+ ->expects($this->once())
+ ->method('execute')
+ ->with('index', array('foo' => 'bar'));
+
+ $Crud->executeAction('index', array('foo' => 'bar'));
+ }
+
+/**
+ * testEnable
+ *
+ */
+ public function testEnable() {
+ $this->Crud->mapAction('puppies', 'view', false);
+ $this->Crud->enable('puppies');
+
+ $result = $this->Crud->isActionMapped('puppies');
+ $this->assertTrue($result);
+ }
+
+/**
+ * testDisableAction
+ *
+ */
+ public function testDisableAction() {
+ $this->Crud->disable('view');
+
+ $result = $this->Crud->isActionMapped('view');
+ $this->assertFalse($result);
+ }
+
+/**
+ * testMapAction
+ *
+ */
+ public function testMapAction() {
+ $this->Crud->mapAction('puppies', 'view');
+
+ $result = $this->Crud->isActionMapped('puppies');
+ $this->assertTrue($result);
+
+ $this->Crud->mapAction('kittens', array(
+ 'className' => 'Crud.index',
+ 'relatedModels' => false
+ ));
+
+ $result = $this->Crud->isActionMapped('kittens');
+ $this->assertTrue($result);
+
+ $expected = array(
+ 'className' => 'Crud.index',
+ 'relatedModels' => false
+ );
+ $this->assertEquals($expected, $this->Crud->config('actions.kittens'));
+ }
+
+/**
+ * testView
+ *
+ */
+ public function testView() {
+ $this->request
+ ->expects($this->once())
+ ->method('method')
+ ->will($this->returnValue('GET'));
+
+ $this->controller
+ ->expects($this->once())
+ ->method('render');
+
+ $this->Crud->view('view', 'cupcakes');
+ $this->Crud->execute('view', array(1));
+ }
+
+/**
+ * testIsActionMappedYes
+ *
+ */
+ public function testIsActionMappedYes() {
+ $result = $this->Crud->isActionMapped('index');
+ $this->assertTrue($result);
+
+ $this->controller->action = 'edit';
+ $this->Crud->initialize($this->controller);
+ $result = $this->Crud->isActionMapped();
+ $this->assertTrue($result);
+ }
+
+/**
+ * testIsActionMappedNo
+ *
+ */
+ public function testIsActionMappedNo() {
+ $result = $this->Crud->isActionMapped('puppies');
+ $this->assertFalse($result);
+
+ $this->controller->action = 'rainbows';
+ $this->Crud->initialize($this->controller);
+ $result = $this->Crud->isActionMapped();
+ $this->assertFalse($result);
+ }
+
+/**
+ * Tests on method registers an event
+ *
+ */
+ public function testOn() {
+ $this->Crud->on('event', 'fakeCallback');
+
+ $return = $this->controller->getEventManager()->listeners('Crud.event');
+
+ $expected = array(
+ array(
+ 'callable' => 'fakeCallback',
+ 'passParams' => false
+ )
+ );
+ $this->assertSame($expected, $return);
+ }
+
+/**
+ * tests on method registers an event with extra params
+ *
+ */
+ public function testOnWithPriPriorityy() {
+ $this->Crud->on('event', 'fakeCallback');
+ $this->Crud->on('event', 'fakeHighPriority', array('priority' => 1));
+ $this->Crud->on('event', 'fakeLowPriority', array('priority' => 99999));
+
+ $return = $this->controller->getEventManager()->listeners('Crud.event');
+
+ $expected = array(
+ array(
+ 'callable' => 'fakeHighPriority',
+ 'passParams' => false
+ ),
+ array(
+ 'callable' => 'fakeCallback',
+ 'passParams' => false
+ ),
+ array(
+ 'callable' => 'fakeLowPriority',
+ 'passParams' => false
+ )
+ );
+ $this->assertSame($expected, $return);
+ }
+
+/**
+ * Test if crud complains about unmapped actions
+ *
+ * @expectedException CakeException
+ * @return void
+ */
+ public function testCrudWillComplainAboutUnmappedAction() {
+ $this->Crud->execute('show_all');
+ }
+
+/**
+ * Test if view with array yields the expected result
+ *
+ * @return void
+ */
+ public function testViewWithArrayNewAction() {
+ $this->request
+ ->expects($this->once())
+ ->method('method')
+ ->will($this->returnValue('GET'));
+
+ $this->request
+ ->expects($this->once())
+ ->method('method')
+ ->will($this->returnValue('GET'));
+
+ $this->controller
+ ->expects($this->once())
+ ->method('render')
+ ->with('index');
+
+ $this->Crud->mapAction('show_all', 'index');
+ $this->Crud->view(array('show_all' => 'index', 'index' => 'overview'));
+
+ $this->Crud->execute('show_all');
+ }
+
+/**
+ * Test if view with array yields the expected result
+ *
+ * @return void
+ */
+ public function testViewWithArrayIndexAction() {
+ $this->request
+ ->expects($this->once())
+ ->method('method')
+ ->will($this->returnValue('GET'));
+
+ $this->controller
+ ->expects($this->once())
+ ->method('render')
+ ->with('overview');
+
+ $this->Crud->mapAction('show_all', 'index');
+ $this->Crud->view(array('show_all' => 'index', 'index' => 'overview'));
+
+ $this->Crud->execute('index');
+ }
+
+/**
+ * Test that having no mapped model for an action,
+ * just use the modelClass from the controller
+ *
+ * @return void
+ */
+ public function testSetModelPropertiesDefault() {
+ $this->Crud->setAction('index');
+ $this->Crud->setModelProperties();
+ $this->assertSame('CrudExample', $this->Crud->getModelName());
+ }
+
+/**
+ * Test that the build in action names can't be used
+ * within other plugins
+ *
+ * @expectedException CakeException
+ * @expectedExceptionMessage The build-in CrudActions (Index, View, Add, Edit and Delete) must be loaded from the Crud plugin
+ * @return void
+ */
+ public function testBuildInCrudActionsCantBeUsedInOtherPluginsIndex() {
+ $this->Crud->mapAction('test', 'Sample.Index');
+ }
+
+/**
+ * Test that the build in action names can't be used
+ * within other plugins
+ *
+ * @expectedException CakeException
+ * @expectedExceptionMessage The build-in CrudActions (Index, View, Add, Edit and Delete) must be loaded from the Crud plugin
+ * @return void
+ */
+ public function testBuildInCrudActionsCantBeUsedInOtherPluginsView() {
+ $this->Crud->mapAction('test', 'Sample.View');
+ }
+
+/**
+ * Test that the build in action names can't be used
+ * within other plugins
+ *
+ * @expectedException CakeException
+ * @expectedExceptionMessage The build-in CrudActions (Index, View, Add, Edit and Delete) must be loaded from the Crud plugin
+ * @return void
+ */
+ public function testBuildInCrudActionsCantBeUsedInOtherPluginsAdd() {
+ $this->Crud->mapAction('test', 'Sample.Add');
+ }
+
+/**
+ * Test that the build in action names can't be used
+ * within other plugins
+ *
+ * @expectedException CakeException
+ * @expectedExceptionMessage The build-in CrudActions (Index, View, Add, Edit and Delete) must be loaded from the Crud plugin
+ * @return void
+ */
+ public function testBuildInCrudActionsCantBeUsedInOtherPluginsEdit() {
+ $this->Crud->mapAction('test', 'Sample.Edit');
+ }
+
+/**
+ * Test that the build in action names can't be used
+ * within other plugins
+ *
+ * @expectedException CakeException
+ * @expectedExceptionMessage The build-in CrudActions (Index, View, Add, Edit and Delete) must be loaded from the Crud plugin
+ * @return void
+ */
+ public function testBuildInCrudActionsCantBeUsedInOtherPluginsDelete() {
+ $this->Crud->mapAction('test', 'Sample.Delete');
+ }
+
+/**
+ * Test that Providing a CrudAction name that isn't in the
+ * list of build-in once, will allow you to use it inside
+ * another plugin.
+ *
+ * It's expected that the plugin CrudSample doesn't exist,
+ * the App::uses() where the warning is raised is *after*
+ * the check for the above build-in class names
+ *
+ * @expectedException CakeException
+ * @expectedExceptionMessage Plugin CrudSample could not be found.
+ * @return void
+ */
+ public function testCustomCrudActionsCanBeUsedInPlugins() {
+ $this->Crud->mapAction('test', 'CrudSample.MyDelete');
+ }
+
+/**
+ * Test that having a 'search' action in the controller
+ * and calling ->execute('index') will still
+ * render the 'search' view
+ *
+ * @return void
+ */
+ public function testViewCanBeChangedInControllerAction() {
+ $this->request
+ ->expects($this->once())
+ ->method('method')
+ ->will($this->returnValue('GET'));
+
+ $this->request->action = 'search';
+
+ $this->controller
+ ->expects($this->once())
+ ->method('render')
+ ->with('search');
+
+ $this->controller->search();
+ }
+
+/**
+ * Test the default configuration for CrudComponent
+ *
+ * @return void
+ */
+ public function testDefaultConfig() {
+ $Collection = $this->getMock('ComponentCollection');
+
+ $Crud = new CrudComponent($Collection);
+
+ $result = $Crud->config();
+ $expected = 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
+ );
+ $this->assertEquals($expected, $result);
+ }
+
+/**
+ * Test that providing configuration for a new
+ * listener in the Crud setting should preserve
+ * the defaults and add the new listener to the array
+ *
+ * @return void
+ */
+ public function testConstructMerging() {
+ $Collection = $this->getMock('ComponentCollection');
+
+ $config = array(
+ 'listeners' => array(
+ 'Api' => 'Crud.Api'
+ )
+ );
+
+ $Crud = new CrudComponent($Collection, $config);
+ $result = $Crud->config();
+ $expected = array(
+ 'actions' => array(),
+ 'eventPrefix' => 'Crud',
+ 'listeners' => array(
+ 'RelatedModels' => 'Crud.RelatedModels',
+ 'Api' => 'Crud.Api'
+ ),
+ '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
+ );
+ $this->assertEquals($expected, $result);
+ }
+
+/**
+ * Test that providing configuration for a new
+ * listener in the Crud setting should preserve
+ * the defaults and add the new listener to the array
+ *
+ * @return void
+ */
+ public function testConstructMerging2() {
+ $Collection = $this->getMock('ComponentCollection');
+
+ $config = array(
+ 'listeners' => array(
+ )
+ );
+
+ $Crud = new CrudComponent($Collection, $config);
+ $result = $Crud->config();
+ $expected = 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
+ );
+ $this->assertEquals($expected, $result);
+ }
+
+/**
+ * Test that addListener works - without listener
+ * default config
+ *
+ * @return void
+ */
+ public function testAddListenerWithoutDefaults() {
+ $listeners = $this->Crud->config('listeners');
+ $expected = array(
+ 'RelatedModels' => array('className' => 'Crud.RelatedModels')
+ );
+
+ $this->assertEquals($expected, $listeners);
+
+ $this->Crud->addListener('Api', 'Crud.Api');
+
+ $listeners = $this->Crud->config('listeners');
+ $expected = array(
+ 'RelatedModels' => array('className' => 'Crud.RelatedModels'),
+ 'Api' => array('className' => 'Crud.Api')
+ );
+ $this->assertEquals($expected, $listeners);
+
+ $this->assertEquals(array('className' => 'Crud.Api'), $this->Crud->defaults('listeners', 'Api'));
+ }
+
+/**
+ * Test that addListener works - with listener
+ * default config
+ *
+ * @return void
+ */
+ public function testAddListenerWithDefaults() {
+ $listeners = $this->Crud->config('listeners');
+ $expected = array(
+ 'RelatedModels' => array('className' => 'Crud.RelatedModels')
+ );
+
+ $this->assertEquals($expected, $listeners);
+
+ $this->Crud->addListener('Api', 'Crud.Api', array('test' => 1));
+
+ $listeners = $this->Crud->config('listeners');
+ $expected = array(
+ 'RelatedModels' => array('className' => 'Crud.RelatedModels'),
+ 'Api' => array('className' => 'Crud.Api', 'test' => 1)
+ );
+ $this->assertEquals($expected, $listeners);
+
+ $this->assertEquals(
+ array('className' => 'Crud.Api', 'test' => 1),
+ $this->Crud->defaults('listeners', 'Api')
+ );
+ }
+
+/**
+ * Test that removeListener works
+ *
+ * @return void
+ */
+ public function testRemoveListener() {
+ $listeners = $this->Crud->config('listeners');
+ $expected = array(
+ 'RelatedModels' => array('className' => 'Crud.RelatedModels')
+ );
+
+ $this->assertEquals($expected, $listeners);
+
+ $this->Crud->removeListener('RelatedModels');
+
+ $listeners = $this->Crud->config('listeners');
+ $expected = array();
+ $this->assertEquals($expected, $listeners);
+
+ // Should now throw an exception
+ $this->setExpectedException('CakeException', 'Listener "relatedModels" is not configured');
+ $this->Crud->listener('relatedModels');
+ }
+
+/**
+ * Test removing a listener that doesn't exist
+ * should return false
+ *
+ * @return void
+ */
+ public function testRemoveListenerNoExist() {
+ $expected = false;
+ $result = $this->Crud->removeListener('invalid_name');
+ $this->assertEquals($expected, $result);
+ }
+
+/**
+ * Test that removeLister works
+ *
+ * Also ensure that the listener is detached from EventManager
+ *
+ * @return void
+ */
+ public function testRemoveListenerAttached() {
+ $listeners = $this->Crud->config('listeners');
+ $expected = array(
+ 'RelatedModels' => array('className' => 'Crud.RelatedModels')
+ );
+ $this->assertEquals($expected, $listeners);
+
+ // Make sure the listener is attached
+ $this->Crud->listener('RelatedModels');
+
+ // Remove it (including detach)
+ $this->Crud->removeListener('RelatedModels');
+
+ $listeners = $this->Crud->config('listeners');
+ $expected = array();
+ $this->assertEquals($expected, $listeners);
+
+ // Should now throw an exception
+ $this->setExpectedException('CakeException', 'Listener "RelatedModels" is not configured');
+ $this->Crud->listener('RelatedModels');
+ }
+
+/**
+ * Test changing view var for one action works
+ *
+ * @return void
+ */
+ public function testViewVarSingleAction() {
+ $this->Crud->viewVar('index', 'my_var');
+
+ $expected = 'my_var';
+ $result = $this->Crud->action('index')->viewVar();
+ $this->assertEquals($expected, $result);
+ }
+
+/**
+ * Test changing view var for multiple actions works
+ *
+ * @return void
+ */
+ public function testViewVarMultipleActions() {
+ $this->Crud->viewVar(array('index' => 'my_var', 'view' => 'view_var'));
+
+ $expected = 'my_var';
+ $result = $this->Crud->action('index')->viewVar();
+ $this->assertEquals($expected, $result);
+
+ $expected = 'view_var';
+ $result = $this->Crud->action('view')->viewVar();
+ $this->assertEquals($expected, $result);
+ }
+
+/**
+ * Test changing view var for multiple actions works
+ *
+ * @return void
+ */
+ public function testFindMethodMultipleActions() {
+ $this->Crud->findMethod(array('index' => 'my_all', 'view' => 'my_view'));
+
+ $expected = 'my_all';
+ $result = $this->Crud->action('index')->findMethod();
+ $this->assertEquals($expected, $result);
+
+ $expected = 'my_view';
+ $result = $this->Crud->action('view')->findMethod();
+ $this->assertEquals($expected, $result);
+ }
+
+/**
+ * Test setting defaults for one action works
+ *
+ * @return void
+ */
+ public function testDefaultsOnAction() {
+ $this->Crud->defaults('actions', 'index', array('unit_test' => true));
+ $config = $this->Crud->defaults('actions', 'index');
+
+ $this->assertTrue($config['unit_test']);
+ }
+
+/**
+ * Test setting defaults for multiple actions work
+ *
+ * @return void
+ */
+ public function testDefaultsMultipleActions() {
+ $this->Crud->defaults('actions', array('index', 'view'), array('unit_test' => true));
+
+ $config = $this->Crud->defaults('actions', 'index');
+ $this->assertTrue($config['unit_test']);
+
+ $config = $this->Crud->defaults('actions', 'view');
+ $this->assertTrue($config['unit_test']);
+ }
+
+/**
+ * Test setting defaults for one listener works
+ *
+ * @return void
+ */
+ public function testDefaultsOneListener() {
+ $this->Crud->defaults('listeners', 'translations', array('unit_test' => true));
+ $config = $this->Crud->defaults('listeners', 'translations');
+
+ $this->assertTrue($config['unit_test']);
+ }
+
+/**
+ * Test setting defaults for multiple actions work
+ *
+ * @return void
+ */
+ public function testDefaultsMultipleListeners() {
+ $this->Crud->defaults('listeners', array('translations', 'relatedModels'), array('unit_test' => true));
+
+ $config = $this->Crud->defaults('listeners', 'translations');
+ $this->assertTrue($config['unit_test']);
+
+ $config = $this->Crud->defaults('listeners', 'relatedModels');
+ $this->assertTrue($config['unit_test']);
+ }
+
+/**
+ * Test setting defaults for one listener works
+ *
+ * This proves that not setting 'className' doesn't break
+ *
+ * @return void
+ */
+ public function testDefaultsListenerNotAlreadyLoaded() {
+ $this->Crud->defaults('listeners', 'api', array('unit_test' => true));
+ $config = $this->Crud->defaults('listeners', 'api');
+ $this->assertTrue($config['unit_test']);
+ }
+
+/**
+ * Test adding a listener only by a name and no class works
+ *
+ * By only providing a name, it should default to Crud plugin
+ *
+ * @return void
+ */
+ public function testAddListenerOnlyNameNoClassName() {
+ $this->Crud->addListener('api');
+ $config = $this->Crud->config('listeners');
+ $this->assertEquals(array('className' => 'Crud.Api'), $config['api']);
+ }
+
+/**
+ * Test adding a listener only by a name and a class works
+ *
+ * By providing a class, it should not default to Crud plugin
+ * even though it doesn't contain any plugin.
+ *
+ * This allow developers to put listeners in app/Controller/Crud
+ *
+ * @return void
+ */
+ public function testAddListenerOnlyNameClassName() {
+ $this->Crud->addListener('api', 'Api');
+ $config = $this->Crud->config('listeners');
+ $this->assertEquals(array('className' => 'Api'), $config['api']);
+ }
+
+/**
+ * Test adding a listener only by its name, with plugin dot syntax
+ * works
+ *
+ * @return void
+ */
+ public function testAddListenerOnlyNameWithPlugin() {
+ $this->Crud->addListener('MyPlugin.Api');
+ $config = $this->Crud->config('listeners');
+ $this->assertEquals(array('className' => 'MyPlugin.Api'), $config['api']);
+ }
+
+/**
+ * Test adding a listener only by its name, with plugin dot syntax
+ * works
+ *
+ * @return void
+ */
+ public function testAddListenerOnlyNameWithPluginLowercase() {
+ $this->Crud->addListener('MyPlugin.api');
+ $config = $this->Crud->config('listeners');
+ $this->assertEquals(array('className' => 'MyPlugin.Api'), $config['api']);
+ }
+
+/**
+ * Test the Crud sets model and modelClass to NULL
+ * if there is no model defined in the controller
+ *
+ * @return void
+ */
+ public function testControllerWithEmptyUses() {
+ $controller = new Controller(new CakeRequest());
+ $this->Crud = new CrudComponent($this->Collection, array('actions' => array('index')));
+ $this->Crud->initialize($controller);
+ $this->controller->Crud = $this->Crud;
+ $this->Crud->action('index');
+ $subject = $this->Crud->trigger('sample');
+
+ $this->assertNull($subject->model);
+ $this->assertNull($subject->modelClass);
+ }
+
+/**
+ * Test that it's possible to change just one sub key
+ * by providing all the parents, without loosing any
+ * default settings
+ *
+ * @return void
+ */
+ public function testConfigMergeWorks() {
+ $this->Crud->config(array('messages' => array('invalidId' => array('code' => 500))));
+
+ $expected = array(
+ 'code' => 500,
+ 'class' => 'BadRequestException',
+ 'text' => 'Invalid id'
+ );
+ $result = $this->Crud->config('messages.invalidId');
+ $this->assertEquals($expected, $result);
+ }
+
+/**
+ * Using $key and value, and specifying no merge should overwrite the value keys
+ *
+ * @return void
+ */
+ public function testConfigOverwrite() {
+ $this->Crud->config('messages', array('invalidId' => array('code' => 500)), null, false);
+
+ $expected = array(
+ 'domain' => 'crud',
+ 'invalidId' => array(
+ 'code' => 500
+ ),
+ 'recordNotFound' => array(
+ 'code' => 404,
+ 'class' => 'NotFoundException',
+ 'text' => 'Not found'
+ ),
+ 'badRequestMethod' => array(
+ 'code' => 405,
+ 'class' => 'MethodNotAllowedException',
+ 'text' => 'Method not allowed. This action permits only {methods}'
+ )
+ );
+ $result = $this->Crud->config('messages');
+ $this->assertEquals($expected, $result);
+ }
+/**
+ * Passing an array, and specifying no merge should overwrite the value keys
+ *
+ * @return void
+ */
+ public function testConfigOverwriteArray() {
+ $this->Crud->config(array('messages' => array('invalidId' => array('code' => 500))), null, false);
+
+ $expected = array(
+ 'domain' => 'crud',
+ 'invalidId' => array(
+ 'code' => 500,
+ '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}'
+ )
+ );
+ $result = $this->Crud->config('messages');
+ $this->assertEquals($expected, $result);
+ }
+
+/**
+ * Tests that is possible to set the model class to use for the action
+ *
+ * @return void
+ */
+ public function testUseModel() {
+ $controller = new Controller(new CakeRequest());
+ $this->Crud = new CrudComponent($this->Collection, array('actions' => array('index')));
+ $this->Crud->initialize($controller);
+ $this->controller->Crud = $this->Crud;
+ $class = $this->getMockClass('Model');
+ $this->Crud->useModel($class);
+ $this->Crud->action('index');
+ $subject = $this->Crud->trigger('sample');
+
+ $this->assertInstanceOf($class, $subject->model);
+ $this->assertEquals($class, $subject->modelClass);
+ }
+
+/**
+ * test_loadListener
+ *
+ * @return void
+ */
+ public function test_loadListener() {
+ $this->Crud->config('listeners.HasSetup', array(
+ 'className' => 'Test'
+ ));
+
+ $this->setReflectionClassInstance($this->Crud);
+ $listener = $this->callProtectedMethod('_loadListener', array('HasSetup'), $this->Crud);
+ $this->assertSame(1, $listener->callCount, 'Setup should be called');
+ }
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Component/models.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Component/models.php
new file mode 100644
index 000000000..c1fefb8d4
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Component/models.php
@@ -0,0 +1,58 @@
+ 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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/AddCrudActionTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/AddCrudActionTest.php
new file mode 100644
index 000000000..c460dde64
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/AddCrudActionTest.php
@@ -0,0 +1,306 @@
+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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/DeleteCrudActionTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/DeleteCrudActionTest.php
new file mode 100644
index 000000000..e869bd446
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/DeleteCrudActionTest.php
@@ -0,0 +1,431 @@
+_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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/EditCrudActionTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/EditCrudActionTest.php
new file mode 100644
index 000000000..c11569065
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/EditCrudActionTest.php
@@ -0,0 +1,1104 @@
+ array('Model.id' => 1));
+ $data = array('Model' => array('id' => 1));
+
+ $Request = $this->getMock('CakeRequest');
+
+ $Model = $this
+ ->getMock('Model', array('create', 'find', 'escapeField'));
+ $Model
+ ->expects($this->once())
+ ->method('escapeField')
+ ->with()
+ ->will($this->returnValue('Model.id'));
+ $Model
+ ->expects($this->once())
+ ->method('find')
+ ->with('first', $query)
+ ->will($this->returnValue($data));
+
+ $i = 0;
+ $Action = $this
+ ->getMockBuilder('EditCrudAction')
+ ->disableOriginalConstructor()
+ ->setMethods(array('_validateId', '_request', '_model', '_trigger', '_getFindMethod'))
+ ->getMock();
+ $Action
+ ->expects($this->at($i++))
+ ->method('_validateId')
+ ->with(1)
+ ->will($this->returnValue(true));
+ $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('_model')
+ ->will($this->returnValue($Model));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_getFindMethod')
+ ->will($this->returnValue('first'));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('beforeFind', array('findMethod' => 'first', 'query' => $query))
+ ->will($this->returnValue(new CrudSubject(array('query' => $query, 'findMethod' => 'first'))));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('afterFind', array('id' => 1, 'item' => $data))
+ ->will($this->returnValue(new CrudSubject(array('item' => $data))));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('beforeRender');
+
+ $this->setReflectionClassInstance($Action);
+ $this->callProtectedMethod('_get', array(1), $Action);
+ }
+
+/**
+ * Test that calling HTTP PUT on an edit action
+ * will trigger the appropriate events and try to
+ * update a record in the database
+ *
+ * This test assumes the best possible case
+ * The id provided, it's correct and it's in the db
+ *
+ * @return void
+ */
+ public function testActionPut() {
+ $Action = $this->_actionSuccess();
+ $this->setReflectionClassInstance($Action);
+ $this->callProtectedMethod('_put', array(1), $Action);
+ }
+
+/**
+ * Test that calling HTTP POST on an edit action
+ * will trigger the appropriate events and try to
+ * update a record in the database
+ *
+ * This test assumes the best possible case
+ * The id provided, it's correct and it's in the db
+ *
+ * @return void
+ */
+ public function testActionPost() {
+ $Action = $this->_actionSuccess();
+ $this->setReflectionClassInstance($Action);
+ $this->callProtectedMethod('_post', array(1), $Action);
+ }
+
+ protected function _actionSuccess() {
+ $data = array('Model' => array('id' => 1));
+
+ $CrudSubject = new CrudSubject();
+
+ $Request = $this->getMock('CakeRequest');
+ $Request->data = $data;
+
+ $Model = $this
+ ->getMockBuilder('Model')
+ ->setMethods(array('saveAssociated', 'find', 'escapeField'))
+ ->setConstructorArgs(array(array('name' => 'Model')))
+ ->getMock();
+
+ $i = 0;
+ $Action = $this
+ ->getMockBuilder('EditCrudAction')
+ ->disableOriginalConstructor()
+ ->setMethods(array(
+ '_validateId', '_request', '_model', '_trigger',
+ '_redirect', 'setFlash', 'saveOptions'
+ ))
+ ->getMock();
+ $Action
+ ->expects($this->at($i++))
+ ->method('_validateId')
+ ->with(1)
+ ->will($this->returnValue(true));
+ $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('_model')
+ ->will($this->returnValue($Model));
+ $Model
+ ->expects($this->once())
+ ->method('escapeField')
+ ->with()
+ ->will($this->returnValue('Model.id'));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('beforeFind', array('query' => array('conditions' => array('Model.id' => 1)), 'findMethod' => 'count'))
+ ->will($this->returnValue(new CrudSubject(array('query' => array('conditions' => array('Model.id' => 1)), 'findMethod' => 'count'))));
+ $Model
+ ->expects($this->once())
+ ->method('find')
+ ->with('count', array('conditions' => array('Model.id' => 1)))
+ ->will($this->returnValue(true));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('beforeSave', array('id' => 1));
+ $Action
+ ->expects($this->at($i++))
+ ->method('saveOptions')
+ ->will($this->returnValue(array('atomic' => true)));
+ $Model
+ ->expects($this->once())
+ ->method('saveAssociated')
+ ->with($data)
+ ->will($this->returnValue(true));
+ $Action
+ ->expects($this->at($i++))
+ ->method('setFlash')
+ ->with('success');
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('afterSave', array('success' => true, 'created' => false, 'id' => 1))
+ ->will($this->returnValue($CrudSubject));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_redirect')
+ ->with($CrudSubject, array('action' => 'index'));
+ $Action
+ ->expects($this->exactly(3))
+ ->method('_trigger');
+ return $Action;
+ }
+
+/**
+ * Test that calling HTTP PUT on an edit action
+ * will trigger the appropriate events and try to
+ * update a record in the database
+ *
+ * This test assumes the saveAssociated() call fails
+ * The id provided, it's correct and it's in the db
+ *
+ * @return void
+ */
+ public function testActionPutSaveError() {
+ $data = array('Model' => array('id' => 1));
+
+ $CrudSubject = new CrudSubject();
+
+ $Request = $this->getMock('CakeRequest');
+ $Request->data = $data;
+
+ $Model = $this
+ ->getMockBuilder('Model')
+ ->setMethods(array('saveAssociated'))
+ ->setConstructorArgs(array(array('name' => 'Model')))
+ ->getMock();
+
+ $i = 0;
+ $Action = $this
+ ->getMockBuilder('EditCrudAction')
+ ->disableOriginalConstructor()
+ ->setMethods(array(
+ '_validateId', '_request', '_model', '_trigger',
+ '_redirect', 'setFlash', 'saveOptions', '_findRecord'
+ ))
+ ->getMock();
+ $Action
+ ->expects($this->at($i++))
+ ->method('_validateId')
+ ->with(1)
+ ->will($this->returnValue(true));
+ $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('_findRecord')
+ ->with(1, 'count')
+ ->will($this->returnValue(true));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('beforeSave', array('id' => 1));
+ $Action
+ ->expects($this->at($i++))
+ ->method('saveOptions')
+ ->will($this->returnValue(array('atomic' => true)));
+ $Model
+ ->expects($this->once())
+ ->method('saveAssociated')
+ ->with($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, 'id' => 1))
+ ->will($this->returnValue($CrudSubject));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('beforeRender');
+ $Action
+ ->expects($this->never())
+ ->method('_redirect');
+ $Action
+ ->expects($this->exactly(3))
+ ->method('_trigger');
+
+ $this->setReflectionClassInstance($Action);
+ $this->callProtectedMethod('_put', array(1), $Action);
+ }
+
+/**
+ * Test that calling HTTP GET on an edit action
+ * will trigger the appropriate events
+ *
+ * Given an ID, we test what happens if the ID doesn't
+ * exist in the database
+ *
+ * @expectedException NotFoundException
+ * @expectedExceptionMessage Not Found
+ * @expectedExceptionCode 404
+ * @return void
+ */
+ public function testActionGetWithNonexistingId() {
+ $CrudSubject = new CrudSubject();
+
+ $query = array('conditions' => array('Model.id' => 1));
+
+ $Request = $this->getMock('CakeRequest');
+
+ $Model = $this
+ ->getMock('Model', array('escapeField', 'find'));
+
+ $i = 0;
+ $Action = $this
+ ->getMockBuilder('EditCrudAction')
+ ->disableOriginalConstructor()
+ ->setMethods(array(
+ '_validateId', '_request', '_model', '_trigger',
+ '_redirect', 'setFlash', 'saveOptions', 'message'
+ ))
+ ->getMock();
+ $Action
+ ->expects($this->at($i++))
+ ->method('_validateId')
+ ->with(1)
+ ->will($this->returnValue(true));
+ $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('_model')
+ ->will($this->returnValue($Model));
+ $Model
+ ->expects($this->once())
+ ->method('escapeField')
+ ->with()
+ ->will($this->returnValue('Model.id'));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('beforeFind', array('findMethod' => 'first', 'query' => $query))
+ ->will($this->returnValue(new CrudSubject(array('query' => $query, 'findMethod' => 'first'))));
+ $Model
+ ->expects($this->once())
+ ->method('find')
+ ->with('first', $query)
+ ->will($this->returnValue(array()));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('recordNotFound', array('id' => 1))
+ ->will($this->returnValue($CrudSubject));
+ $Action
+ ->expects($this->at($i++))
+ ->method('message')
+ ->with('recordNotFound', array('id' => 1))
+ ->will($this->returnValue(array('class' => 'NotFoundException', 'text' => 'Not Found', 'code' => 404)));
+ $Action
+ ->expects($this->exactly(2))
+ ->method('_trigger');
+
+ $this->setReflectionClassInstance($Action);
+ $this->callProtectedMethod('_get', array(1), $Action);
+ }
+
+/**
+ * Test that calling HTTP GET on an edit action
+ * will trigger the appropriate events
+ *
+ * Given an ID, we test what happens if the ID is invalid
+ *
+ * @return void
+ */
+ public function testActionGetWithInvalidId() {
+ $i = 0;
+ $Action = $this
+ ->getMockBuilder('EditCrudAction')
+ ->disableOriginalConstructor()
+ ->setMethods(array(
+ '_validateId'
+ ))
+ ->getMock();
+ $Action
+ ->expects($this->at($i++))
+ ->method('_validateId')
+ ->with(null)
+ ->will($this->returnValue(false));
+
+ $this->setReflectionClassInstance($Action);
+ $this->callProtectedMethod('_get', array(null), $Action);
+ }
+
+/**
+ * Test that calling HTTP PUT on an edit action
+ * will trigger the appropriate events
+ *
+ * Given an ID, we test what happens if the ID is invalid
+ *
+ * @return void
+ */
+ public function testActionPutWithInvalidId() {
+ $i = 0;
+ $Action = $this
+ ->getMockBuilder('EditCrudAction')
+ ->disableOriginalConstructor()
+ ->setMethods(array(
+ '_validateId'
+ ))
+ ->getMock();
+ $Action
+ ->expects($this->at($i++))
+ ->method('_validateId')
+ ->with(null)
+ ->will($this->returnValue(false));
+
+ $this->setReflectionClassInstance($Action);
+ $this->callProtectedMethod('_put', array(null), $Action);
+ }
+
+/**
+ * Test that calling HTTP GET on an edit 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
+ * Additionally the `_getFindMethod` method returns
+ * something not-default
+ *
+ * @return void
+ */
+ public function testGetWithCustomFindMethod() {
+ $query = array('conditions' => array('Model.id' => 1));
+ $data = array('Model' => array('id' => 1));
+
+ $Request = $this->getMock('CakeRequest');
+
+ $Model = $this
+ ->getMock('Model', array('create', 'find', 'escapeField'));
+
+ $i = 0;
+ $Action = $this
+ ->getMockBuilder('EditCrudAction')
+ ->disableOriginalConstructor()
+ ->setMethods(array('_validateId', '_request', '_model', '_trigger', '_getFindMethod'))
+ ->getMock();
+ $Action
+ ->expects($this->at($i++))
+ ->method('_validateId')
+ ->with(1)
+ ->will($this->returnValue(true));
+ $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('_model')
+ ->will($this->returnValue($Model));
+ $Model
+ ->expects($this->once())
+ ->method('escapeField')
+ ->with()
+ ->will($this->returnValue('Model.id'));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_getFindMethod')
+ ->will($this->returnValue('first'));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('beforeFind', array('findMethod' => 'first', 'query' => $query))
+ ->will($this->returnValue(new CrudSubject(array('query' => $query, 'findMethod' => 'myCustomQuery'))));
+ $Model
+ ->expects($this->once())
+ ->method('find')
+ ->with('myCustomQuery', $query)
+ ->will($this->returnValue($data));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('afterFind', array('id' => 1, 'item' => $data))
+ ->will($this->returnValue(new CrudSubject(array('item' => $data))));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('beforeRender');
+
+ $this->setReflectionClassInstance($Action);
+ $this->callProtectedMethod('_get', array(1), $Action);
+ }
+
+/**
+ * test_findRecordDefault
+ *
+ * @return void
+ */
+ public function test_findRecordDefault() {
+ $query = array('conditions' => array('Model.id' => 1));
+ $findParams = array('findMethod' => 'special', 'query' => $query);
+
+ $i = 0;
+ $Model = $this->getMock('Model', array('escapeField', 'find'));
+ $Model
+ ->expects($this->at($i++))
+ ->method('escapeField')
+ ->will($this->returnValue('Model.id'));
+ $Model
+ ->expects($this->at($i++))
+ ->method('find')
+ ->with('special', $query);
+
+ $i = 0;
+ $Action = $this
+ ->getMockBuilder('EditCrudAction')
+ ->disableOriginalConstructor()
+ ->setMethods(array('_model', '_getFindMethod', '_trigger'))
+ ->getMock();
+ $Action
+ ->expects($this->at($i++))
+ ->method('_model')
+ ->will($this->returnValue($Model));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_getFindMethod')
+ ->will($this->returnValue('special'));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('beforeFind', $findParams)
+ ->will($this->returnValue(new CrudSubject($findParams)));
+
+ $this->setReflectionClassInstance($Action);
+ $this->callProtectedMethod('_findRecord', array(1), $Action);
+ }
+
+/**
+ * test_findRecordOverride
+ *
+ * @return void
+ */
+ public function test_findRecordOverride() {
+ $query = array('conditions' => array('Model.id' => 1));
+ $findParams = array('findMethod' => 'count', 'query' => $query);
+
+ $i = 0;
+ $Model = $this->getMock('Model', array('escapeField', 'find'));
+ $Model
+ ->expects($this->at($i++))
+ ->method('escapeField')
+ ->will($this->returnValue('Model.id'));
+ $Model
+ ->expects($this->at($i++))
+ ->method('find')
+ ->with('count', $query);
+
+ $i = 0;
+ $Action = $this
+ ->getMockBuilder('EditCrudAction')
+ ->disableOriginalConstructor()
+ ->setMethods(array('_model', '_getFindMethod', '_trigger'))
+ ->getMock();
+ $Action
+ ->expects($this->at($i++))
+ ->method('_model')
+ ->will($this->returnValue($Model));
+ $Action
+ ->expects($this->never())
+ ->method('_getFindMethod');
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('beforeFind', $findParams)
+ ->will($this->returnValue(new CrudSubject($findParams)));
+
+ $this->setReflectionClassInstance($Action);
+ $this->callProtectedMethod('_findRecord', array(1, 'count'), $Action);
+ }
+
+/**
+ * testPutSetsIdFromUrl
+ *
+ * @return void
+ */
+ public function testPutSetsIdFromUrl() {
+ $query = array('conditions' => array('Model.id' => 1));
+ $findParams = array('findMethod' => 'count', 'query' => $query);
+
+ $data = array('Model' => array('some' => 'data'));
+
+ $Request = $this->getMock('CakeRequest');
+ $Request->data = $data;
+ $Request->params['pass'][0] = 1;
+
+ $Controller = $this
+ ->getMockBuilder('Controller')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $Model = $this
+ ->getMockBuilder('Model')
+ ->setMethods(array('saveAssociated', 'find', 'escapeField'))
+ ->setConstructorArgs(array(array('name' => 'Model')))
+ ->getMock();
+
+ $i = $j = 0;
+ $Action = $this
+ ->getMockBuilder('EditCrudAction')
+ ->disableOriginalConstructor()
+ ->setMethods(array('_validateId', '_request', '_model', '_findRecord', '_trigger', 'setFlash'))
+ ->getMock();
+ $Action
+ ->expects($this->at($i++))
+ ->method('_validateId')
+ ->will($this->returnValue(true));
+ $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('_findRecord')
+ ->will($this->returnValue(true));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('beforeSave', array('id' => 1))
+ ->will($this->returnValue(new CrudSubject(array('id' => 1))));
+ $Model
+ ->expects($this->at($j++))
+ ->method('saveAssociated')
+ ->with(array('Model' => array('id' => 1, 'some' => 'data')), array('validate' => 'first', 'atomic' => true));
+
+ $this->setReflectionClassInstance($Action);
+ $this->callProtectedMethod('_put', array(1), $Action);
+ }
+
+/**
+ * testPutSetsIdFromUrlWithAbreviatedData
+ *
+ * @return void
+ */
+ public function testPutSetsIdFromUrlWithAbreviatedData() {
+ $query = array('conditions' => array('Model.id' => 1));
+ $findParams = array('findMethod' => 'count', 'query' => $query);
+
+ $data = array('some' => 'data');
+
+ $Request = $this->getMock('CakeRequest');
+ $Request->data = $data;
+ $Request->params['pass'][0] = 1;
+
+ $Controller = $this
+ ->getMockBuilder('Controller')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $Model = $this
+ ->getMockBuilder('Model')
+ ->setMethods(array('saveAssociated', 'find', 'escapeField'))
+ ->setConstructorArgs(array(array('name' => 'Model')))
+ ->getMock();
+
+ $i = $j = 0;
+ $Action = $this
+ ->getMockBuilder('EditCrudAction')
+ ->disableOriginalConstructor()
+ ->setMethods(array('_validateId', '_request', '_model', '_findRecord', '_trigger', 'setFlash'))
+ ->getMock();
+ $Action
+ ->expects($this->at($i++))
+ ->method('_validateId')
+ ->will($this->returnValue(true));
+ $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('_findRecord')
+ ->will($this->returnValue(true));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_trigger')
+ ->with('beforeSave', array('id' => 1))
+ ->will($this->returnValue(new CrudSubject(array('id' => 1))));
+ $Model
+ ->expects($this->at($j++))
+ ->method('saveAssociated')
+ ->with(array('id' => 1, 'some' => 'data'), array('validate' => 'first', 'atomic' => true));
+
+ $this->setReflectionClassInstance($Action);
+ $this->callProtectedMethod('_put', array(1), $Action);
+ }
+
+/**
+ * test_validateId
+ *
+ * @return void
+ */
+ public function test_validateId() {
+ $Request = $this->getMock('CakeRequest');
+ $Request->data = null;
+ $Request->params['pass'][0] = 1;
+
+ $i = $j = 0;
+ $Action = $this
+ ->getMockBuilder('EditCrudAction')
+ ->disableOriginalConstructor()
+ ->setMethods(array('_request', '_model', '_trigger', 'message'))
+ ->getMock();
+ $Action->config('validateId', false);
+ $Action
+ ->expects($this->at($i++))
+ ->method('_request')
+ ->will($this->returnValue($Request));
+
+ $this->setReflectionClassInstance($Action);
+ $return = $this->callProtectedMethod('_validateId', array(1), $Action);
+ $this->assertTrue($return, 'If there\'s no data, there should be no data check');
+ }
+
+/**
+ * test_validateIdMatches
+ *
+ * @return void
+ */
+ public function test_validateIdMatches() {
+ $Request = $this->getMock('CakeRequest');
+ $Request->data = array('Model' => array('id' => '1'));
+ $Request->params['pass'][0] = 1;
+
+ $Model = $this
+ ->getMockBuilder('Model')
+ ->setMethods(array('saveAssociated', 'find', 'escapeField'))
+ ->setConstructorArgs(array(array('name' => 'Model')))
+ ->getMock();
+
+ $i = $j = 0;
+ $Action = $this
+ ->getMockBuilder('EditCrudAction')
+ ->disableOriginalConstructor()
+ ->setMethods(array('_request', '_model', '_trigger', 'message'))
+ ->getMock();
+ $Action->config('validateId', false);
+ $Action
+ ->expects($this->at($i++))
+ ->method('_request')
+ ->will($this->returnValue($Request));
+ $Action
+ ->expects($this->at($i++))
+ ->method('_model')
+ ->will($this->returnValue($Model));
+
+ $this->setReflectionClassInstance($Action);
+ $return = $this->callProtectedMethod('_validateId', array(1), $Action);
+ $this->assertTrue($return, 'If there\'s data and it matches, there should be no exception');
+ }
+
+/**
+ * test_validateIdManipulated
+ *
+ * @expectedException BadRequestException
+ * @expectedExceptionMessage Invalid id
+ * @expectedExceptionCode 400
+ *
+ * @return void
+ */
+ public function test_validateIdManipulated() {
+ $data = array('Model' => array('id' => 'manipulated', 'some' => 'data'));
+
+ $Request = new CakeRequest();
+ $Request->data = $data;
+ $Request->params['pass'][0] = 1;
+
+ $Model = $this
+ ->getMockBuilder('Model')
+ ->setMethods(array('saveAssociated', 'find', 'escapeField'))
+ ->setConstructorArgs(array(array('name' => 'Model')))
+ ->getMock();
+
+ $i = $j = 0;
+ $Action = $this
+ ->getMockBuilder('EditCrudAction')
+ ->disableOriginalConstructor()
+ ->setMethods(array('_request', '_model', '_trigger', 'message'))
+ ->getMock();
+ $Action->config('validateId', false);
+ $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('invalidId', array('id' => 'manipulated'));
+ $Action
+ ->expects($this->at($i++))
+ ->method('message')
+ ->with('invalidId')
+ ->will($this->returnValue(array('class' => 'BadRequestException', 'code' => 400, 'text' => 'Invalid id')));
+
+ $this->setReflectionClassInstance($Action);
+ $this->callProtectedMethod('_validateId', array(1), $Action);
+ }
+
+/**
+ * test_validateIdManipulatedShortData
+ *
+ * @expectedException BadRequestException
+ * @expectedExceptionMessage Invalid id
+ * @expectedExceptionCode 400
+ *
+ * @return void
+ */
+ public function test_validateIdManipulatedShortData() {
+ $data = array('id' => 'manipulated', 'some' => 'data');
+
+ $Request = new CakeRequest();
+ $Request->data = $data;
+ $Request->params['pass'][0] = 1;
+
+ $Model = $this
+ ->getMockBuilder('Model')
+ ->setMethods(array('saveAssociated', 'find', 'escapeField'))
+ ->setConstructorArgs(array(array('name' => 'Model')))
+ ->getMock();
+
+ $i = $j = 0;
+ $Action = $this
+ ->getMockBuilder('EditCrudAction')
+ ->disableOriginalConstructor()
+ ->setMethods(array('_request', '_model', '_trigger', 'message'))
+ ->getMock();
+ $Action->config('validateId', false);
+ $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('invalidId', array('id' => 'manipulated'));
+ $Action
+ ->expects($this->at($i++))
+ ->method('message')
+ ->with('invalidId')
+ ->will($this->returnValue(array('class' => 'BadRequestException', 'code' => 400, 'text' => 'Invalid id')));
+
+ $this->setReflectionClassInstance($Action);
+ $this->callProtectedMethod('_validateId', array(1), $Action);
+ }
+
+/**
+ * Verify that _injectPrimaryKey is called, and the result is passed to saveAssociated
+ *
+ * @return void
+ */
+ public function test_injectPrimaryKeyIsCalled() {
+ $CrudSubject = new CrudSubject();
+
+ $Request = $this->getMock('CakeRequest');
+ $Request->data = array('fake', 'input', 'data');
+
+ $Model = $this
+ ->getMockBuilder('Model')
+ ->setMethods(array('saveAssociated', 'find', 'escapeField'))
+ ->setConstructorArgs(array(array('name' => 'Model')))
+ ->getMock();
+ $Model
+ ->expects($this->any())
+ ->method('saveAssociated')
+ ->with(array('id' => 1, '_injectPrimaryKey' => 'return'))
+ ->will($this->returnValue(false));
+
+ $i = 0;
+ $Action = $this
+ ->getMockBuilder('EditCrudAction')
+ ->disableOriginalConstructor()
+ ->setMethods(array('_validateId', '_request', '_model', '_findRecord', '_injectPrimaryKey', 'setFlash', '_trigger'))
+ ->getMock();
+ $Action
+ ->expects($this->any())
+ ->method('_validateId')
+ ->will($this->returnValue(true));
+ $Action
+ ->expects($this->any())
+ ->method('_request')
+ ->will($this->returnValue($Request));
+ $Action
+ ->expects($this->any())
+ ->method('_model')
+ ->will($this->returnValue($Model));
+ $Action
+ ->expects($this->any())
+ ->method('_findRecord')
+ ->will($this->returnValue(true));
+ $Action
+ ->expects($this->once())
+ ->method('_injectPrimaryKey')
+ ->will($this->returnValue(array('id' => 1, '_injectPrimaryKey' => 'return')));
+
+ $this->setReflectionClassInstance($Action);
+ $this->callProtectedMethod('_put', array(1), $Action);
+ }
+
+/**
+ * test_injectPrimaryKey
+ *
+ * Check that the model id is injected into the right place
+ *
+ * @dataProvider idInjectionProvider
+ * @param array $data
+ * @param array $expectation
+ */
+ public function test_injectPrimaryKey($data, $expectation = null) {
+ if (!$expectation) {
+ $expectation = $data;
+ }
+
+ $Model = $this
+ ->getMockBuilder('Model')
+ ->setMethods(array('saveAssociated', 'find', 'escapeField'))
+ ->setConstructorArgs(array(array('name' => 'Model')))
+ ->getMock();
+
+ $Action = new EditCrudAction(new CrudSubject());
+
+ $this->setReflectionClassInstance($Action);
+ $return = $this->callProtectedMethod('_injectPrimaryKey', array($data, 1, $Model), $Action);
+ $this->assertSame($expectation, $return, '"id" should be injected in the right place in the save data');
+ }
+
+/**
+ * idInjectionProvider
+ *
+ * Returns sets of data to use in tests.
+ * input
+ * expected result (optional, uses input if absent)
+ *
+ * @return array
+ */
+ public function idInjectionProvider() {
+ return array(
+ array(
+ array(),
+ array('id' => 1)
+ ),
+ array(
+ array('Model' => array('id' => 1, 'some' => 'update'))
+ ),
+ array(
+ array('Model' => array('id' => 'cheating', 'some' => 'update')),
+ array('Model' => array('id' => 1, 'some' => 'update'))
+ ),
+ array(
+ array('Model' => array('some' => 'update')),
+ array('Model' => array('some' => 'update', 'id' => 1))
+ ),
+ array(
+ array('id' => 1, 'some' => 'update')
+ ),
+ array(
+ array('some' => 'update'),
+ array('some' => 'update', 'id' => 1)
+ ),
+ array(
+ array('something' => 'else', 'Model' => array('some' => 'update')),
+ array('something' => 'else', 'Model' => array('some' => 'update', 'id' => 1)),
+ ),
+ array(
+ array('Category' => array('Category' => array(1))),
+ array(
+ 'Category' => array('Category' => array(1)),
+ 'Model' => array('id' => 1)
+ ),
+ ),
+
+ );
+ }
+
+/**
+ * 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'] = 'edit';
+ $Request->data = array('_add' => 'something');
+
+ $Controller->__construct($Request, new CakeResponse);
+
+ $Crud->__construct(new ComponentCollection);
+ $Crud->initialize($Controller);
+ $Crud->mapAction('edit', 'edit');
+
+ $Crud->addListener('redirect');
+ $Crud->listener('redirect');
+
+ $Action = $Crud->action('edit');
+
+ $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' => 'add');
+ $this->assertEquals($expected, $CrudSubject->url);
+ }
+
+/**
+ * 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'] = 'edit';
+ $Request->data = array('_edit' => 'something');
+
+ $Controller->__construct($Request, new CakeResponse);
+
+ $Crud->__construct(new ComponentCollection);
+ $Crud->initialize($Controller);
+ $Crud->mapAction('edit', 'edit');
+
+ $Crud->addListener('redirect');
+ $Crud->listener('redirect');
+
+ $Action = $Crud->action('edit');
+
+ $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);
+ }
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/IndexCrudActionTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/IndexCrudActionTest.php
new file mode 100644
index 000000000..04af1ee47
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/IndexCrudActionTest.php
@@ -0,0 +1,341 @@
+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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/ViewCrudActionTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/ViewCrudActionTest.php
new file mode 100644
index 000000000..16330af00
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/ViewCrudActionTest.php
@@ -0,0 +1,365 @@
+ 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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudActionTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudActionTest.php
new file mode 100644
index 000000000..c009b0b53
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudActionTest.php
@@ -0,0 +1,989 @@
+ 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');
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudListenerTest.php
new file mode 100644
index 000000000..cb89eb253
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudListenerTest.php
@@ -0,0 +1,19 @@
+assertTrue(true);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudSubjectTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudSubjectTest.php
new file mode 100644
index 000000000..ab062d7fb
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudSubjectTest.php
@@ -0,0 +1,83 @@
+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');
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiFieldFilterListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiFieldFilterListenerTest.php
new file mode 100644
index 000000000..364a98f34
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiFieldFilterListenerTest.php
@@ -0,0 +1,438 @@
+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']);
+ }
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiListenerTest.php
new file mode 100644
index 000000000..5270574f8
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiListenerTest.php
@@ -0,0 +1,1450 @@
+_config = Configure::read();
+ }
+
+ public function tearDown() {
+ parent::tearDown();
+ Configure::write($this->_config);
+ CakePlugin::unload('TestPlugin');
+ }
+
+/**
+ * testBeforeHandle
+ *
+ * @return void
+ */
+ public function testBeforeHandle() {
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('_request', 'registerExceptionHandler', '_checkRequestMethods'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request = $this
+ ->getMockBuilder('CakeRequest')
+ ->setMethods(array('is'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $listener
+ ->expects($this->at(0))
+ ->method('_request')
+ ->will($this->returnValue($request));
+ $request
+ ->expects($this->once())
+ ->method('is')
+ ->with('api')
+ ->will($this->returnValue(true));
+ $listener
+ ->expects($this->at(1))
+ ->method('registerExceptionHandler');
+ $listener
+ ->expects($this->at(1))
+ ->method('_checkRequestMethods');
+
+ $listener->beforeHandle(new CakeEvent('Crud.beforeHandle'));
+ }
+
+/**
+ * testSetup
+ *
+ * @return void
+ */
+ public function testSetup() {
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('setupDetectors', 'registerExceptionHandler'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $listener
+ ->expects($this->at(0))
+ ->method('setupDetectors');
+
+ $listener
+ ->expects($this->at(1))
+ ->method('registerExceptionHandler');
+
+ $listener->setup();
+ }
+
+/**
+ * testBeforeHandleNotApi
+ *
+ * @return void
+ */
+ public function testBeforeHandleNotApi() {
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('_request', '_controller', 'registerExceptionHandler', '_checkRequestMethods'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request = $this
+ ->getMockBuilder('CakeRequest')
+ ->setMethods(array('is'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('getEventManager'))
+ ->getMock();
+
+ $manager = $this
+ ->getMockBuilder('EventManager')
+ ->setMethods(array('detach'))
+ ->getMock();
+
+ $listener
+ ->expects($this->at(0))
+ ->method('_request')
+ ->will($this->returnValue($request));
+ $request
+ ->expects($this->once())
+ ->method('is')
+ ->with('api')
+ ->will($this->returnValue(false));
+ $listener
+ ->expects($this->at(1))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+ $controller
+ ->expects($this->at(0))
+ ->method('getEventManager')
+ ->will($this->returnValue($manager));
+ $manager
+ ->expects($this->at(0))
+ ->method('detach')
+ ->with($listener, 'Crud.setFlash');
+ $manager
+ ->expects($this->at(1))
+ ->method('detach')
+ ->with($listener, 'Crud.beforeRender');
+ $manager
+ ->expects($this->at(2))
+ ->method('detach')
+ ->with($listener, 'Crud.beforeRedirect');
+
+ $listener
+ ->expects($this->never())
+ ->method('registerExceptionHandler');
+ $listener
+ ->expects($this->never())
+ ->method('_checkRequestMethods');
+
+ $listener->beforeHandle(new CakeEvent('Crud.beforeHandle'));
+ }
+
+/**
+ * Test response method
+ *
+ * @return void
+ */
+ public function testResponse() {
+ $request = $this->getMock('CakeRequest', array('is'));
+ $response = $this->getMock('CakeResponse');
+
+ $action = $this->getMock('IndexCrudAction', array('config'), array(new CrudSubject()));
+
+ $subject = $this->getMock('CrudSubject');
+ $subject->success = true;
+
+ $event = new CakeEvent('Crud.afterSave', $subject);
+
+ $i = 0;
+
+ $listener = $this->getMock('ApiListener', array('_request', '_action', 'render'), array($subject));
+ $listener
+ ->expects($this->at($i++))
+ ->method('_action')
+ ->with()
+ ->will($this->returnValue($action));
+ $action
+ ->expects($this->at(0))
+ ->method('config')
+ ->with('api.success')
+ ->will($this->returnValue(array('code' => 200)));
+ $listener
+ ->expects($this->at($i++))
+ ->method('render')
+ ->with($subject)
+ ->will($this->returnValue($response));
+ $response
+ ->expects($this->at(0))
+ ->method('statusCode')
+ ->with(200);
+
+ $result = $listener->respond($event);
+ $this->assertSame($response, $result);
+ }
+
+/**
+ * Test response method with exception config
+ *
+ * @return void
+ */
+ public function testResponseWithExceptionConfig() {
+ $request = $this->getMock('CakeRequest', array('is'));
+ $response = $this->getMock('CakeResponse');
+
+ $action = $this->getMock('IndexCrudAction', array('config'), array(new CrudSubject()));
+
+ $subject = $this->getMock('CrudSubject');
+ $subject->success = true;
+
+ $event = new CakeEvent('Crud.afterSave', $subject);
+
+ $i = 0;
+
+ $listener = $this->getMock('ApiListener', array('_request', '_action', 'render', '_exceptionResponse'), array($subject));
+ $listener
+ ->expects($this->at($i++))
+ ->method('_action')
+ ->with()
+ ->will($this->returnValue($action));
+ $action
+ ->expects($this->at(0))
+ ->method('config')
+ ->with('api.success')
+ ->will($this->returnValue(array('exception' => true)));
+ $listener
+ ->expects($this->at($i++))
+ ->method('_exceptionResponse')
+ ->with(true);
+ $listener
+ ->expects($this->never())
+ ->method('render');
+ $response
+ ->expects($this->never())
+ ->method('statusCode');
+
+ $listener->respond($event);
+ }
+
+/**
+ * Test default configuration
+ *
+ * @return void
+ */
+ public function testDefaultConfiguration() {
+ $listener = new ApiListener(new CrudSubject());
+ $expected = 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
+ )
+ );
+ $result = $listener->config();
+ $this->assertEquals($expected, $result);
+ }
+
+/**
+ * Tests implemented events
+ *
+ * @return void
+ */
+ public function testImplementeEvents() {
+ $subject = $this->getMock('CrudSubject');
+ $apiListener = new ApiListener($subject);
+ $expected = 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)
+ );
+ $this->assertEquals($expected, $apiListener->implementedEvents());
+ }
+
+/**
+ * Data provider for test_exceptionResponse
+ *
+ * @return array
+ */
+ public function data_exceptionResponse() {
+ return array(
+ 'default configuration' => array(
+ array(),
+ 'BadRequestException',
+ 'Unknown error',
+ 0
+ ),
+
+ 'change exception class' => array(
+ array('class' => 'CakeException'),
+ 'CakeException',
+ 'Unknown error',
+ 0
+ ),
+
+ 'change exception code' => array(
+ array('code' => 10),
+ 'BadRequestException',
+ 'Unknown error',
+ 10
+ ),
+
+ 'change exception message' => array(
+ array('message' => 'epic message'),
+ 'BadRequestException',
+ 'epic message',
+ 10
+ ),
+
+ 'Validate case #1 - no validation errors' => array(
+ array('class' => 'CrudValidationException', 'type' => 'validate'),
+ 'CrudValidationException',
+ '0 validation errors occurred',
+ 0
+ ),
+
+ 'Validate case #2 - one validation error' => array(
+ array('class' => 'CrudValidationException', 'type' => 'validate'),
+ 'CrudValidationException',
+ 'A validation error occurred',
+ 0,
+ array(array('id' => 'hello world'))
+ ),
+
+ 'Validate case #3 - two validation errors' => array(
+ array('class' => 'CrudValidationException', 'type' => 'validate'),
+ 'CrudValidationException',
+ '2 validation errors occurred',
+ 0,
+ array(array('id' => 'hello world', 'name' => 'fail me'))
+ )
+ );
+ }
+
+/**
+ * Test _exceptionResponse
+ *
+ * @dataProvider data_exceptionResponse
+ * @param array $apiConfig
+ * @param string $exceptionClass
+ * @param string $exceptionMessage
+ * @param integer $exceptionCode
+ * @param array $validationErrors
+ * @return void
+ */
+ public function test_exceptionResponse($apiConfig, $exceptionClass, $exceptionMessage, $exceptionCode, $validationErrors = array()) {
+ $listener = $this->getMock('ApiListener', array('_validationErrors'), array(new CrudSubject()));
+
+ if (isset($apiConfig['type']) && $apiConfig['type'] === 'validate') {
+ $listener->expects($this->once())->method('_validationErrors')->with()->will($this->returnValue($validationErrors));
+ } else {
+ $listener->expects($this->never())->method('_validationErrors');
+ }
+
+ $this->expectException($exceptionClass, $exceptionMessage, $exceptionCode);
+
+ $this->setReflectionClassInstance($listener);
+ $this->callProtectedMethod('_exceptionResponse', array($apiConfig), $listener);
+ }
+
+/**
+ * Test render
+ *
+ * @return void
+ */
+ public function testRender() {
+ $listener = $this->getMockBuilder('ApiListener')
+ ->setMethods(array('injectViewClasses', '_ensureSuccess', '_ensureData', '_ensureSerialize', '_controller'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subject = new CrudSubject();
+
+ $requestHandler = $this->getMockBuilder('RequestHandlerComponent')
+ ->setMethods(array('renderAs'))
+ ->disableOriginalConstructor()
+ ->getMock();
+ $controller = $this->getMockBuilder('Controller')
+ ->setMethods(array('render'))
+ ->disableOriginalConstructor()
+ ->getMock();
+ $controller->RequestHandler = $requestHandler;
+ $controller->RequestHandler->ext = 'json';
+
+ $i = 0;
+ $listener
+ ->expects($this->at($i++))
+ ->method('injectViewClasses')
+ ->with();
+ $listener
+ ->expects($this->at($i++))
+ ->method('_ensureSuccess')
+ ->with($subject);
+ $listener
+ ->expects($this->at($i++))
+ ->method('_ensureData')
+ ->with($subject);
+ $listener
+ ->expects($this->at($i++))
+ ->method('_ensureSerialize')
+ ->with();
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->with()
+ ->will($this->returnValue($controller));
+ $requestHandler
+ ->expects($this->once())
+ ->method('renderAs')
+ ->with($controller, 'json');
+ $controller
+ ->expects($this->once())
+ ->method('render')
+ ->with();
+
+ $listener->render($subject);
+ }
+
+/**
+ * test_ensureSerializeWithViewVar
+ *
+ * @return void
+ */
+ public function test_ensureSerializeWithViewVar() {
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('_action', '_controller'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('set'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $action = $this
+ ->getMockBuilder('IndexCrudAction')
+ ->setMethods(array('config', 'viewVar'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $i = 0;
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+ $listener
+ ->expects($this->at($i++))
+ ->method('_action')
+ ->will($this->returnValue($action));
+ $action
+ ->expects($this->at(0))
+ ->method('viewVar')
+ ->will($this->returnValue('items'));
+ $controller
+ ->expects($this->once())
+ ->method('set')
+ ->with('_serialize', array('success', 'data' => 'items'));
+
+ $this->setReflectionClassInstance($listener);
+ $this->callProtectedMethod('_ensureSerialize', array(), $listener);
+ }
+
+/**
+ * test_ensureSerializeAlreadySet
+ *
+ * @return void
+ */
+ public function test_ensureSerializeAlreadySet() {
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('_action', '_controller'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('set'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $controller->viewVars['_serialize'] = 'hello world';
+
+ $action = $this
+ ->getMockBuilder('IndexCrudAction')
+ ->setMethods(array('config', 'viewVar'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $i = 0;
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+ $listener
+ ->expects($this->never())
+ ->method('_action');
+ $action
+ ->expects($this->never())
+ ->method('viewVar');
+ $controller
+ ->expects($this->never())
+ ->method('set');
+
+ $this->setReflectionClassInstance($listener);
+ $this->callProtectedMethod('_ensureSerialize', array(), $listener);
+ }
+
+/**
+ * test_ensureSerializeWithViewVarChanged
+ *
+ * @return void
+ */
+ public function test_ensureSerializeWithViewVarChanged() {
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('_action', '_controller'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('set'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $action = $this
+ ->getMockBuilder('IndexCrudAction')
+ ->setMethods(array('config', 'viewVar'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $i = 0;
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+ $listener
+ ->expects($this->at($i++))
+ ->method('_action')
+ ->will($this->returnValue($action));
+ $action
+ ->expects($this->at(0))
+ ->method('viewVar')
+ ->will($this->returnValue('helloWorld'));
+ $controller
+ ->expects($this->once())
+ ->method('set')
+ ->with('_serialize', array('success', 'data' => 'helloWorld'));
+
+ $this->setReflectionClassInstance($listener);
+ $this->callProtectedMethod('_ensureSerialize', array(), $listener);
+ }
+
+/**
+ * test_ensureSerializeWithoutViewVar
+ *
+ * @return void
+ */
+ public function test_ensureSerializeWithoutViewVar() {
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('_action', '_controller'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('set'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $action = $this
+ ->getMockBuilder('AddCrudAction')
+ ->setMethods(array('config'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $i = 0;
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+ $listener
+ ->expects($this->at($i++))
+ ->method('_action')
+ ->will($this->returnValue($action));
+ $controller
+ ->expects($this->once())
+ ->method('set')
+ ->with('_serialize', array('success', 'data'));
+
+ $this->setReflectionClassInstance($listener);
+ $this->callProtectedMethod('_ensureSerialize', array(), $listener);
+ }
+
+/**
+ * test_ensureSuccess
+ *
+ * @return void
+ */
+ public function test_ensureSuccess() {
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('_controller'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subject = new CrudSubject(array('success' => true));
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('set'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $i = 0;
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+ $controller
+ ->expects($this->once())
+ ->method('set')
+ ->with('success', true);
+
+ $this->setReflectionClassInstance($listener);
+ $this->callProtectedMethod('_ensureSuccess', array($subject), $listener);
+ }
+
+/**
+ * test_ensureData
+ *
+ * @return void
+ */
+ public function test_ensureData() {
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('_controller', '_action'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('set'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $action = $this
+ ->getMockBuilder('CrudAction')
+ ->setMethods(array('config'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subject = new CrudSubject(array('success' => true));
+
+ $config = array();
+
+ $i = 0;
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+ $listener
+ ->expects($this->at($i++))
+ ->method('_action')
+ ->will($this->returnValue($action));
+ $action
+ ->expects($this->at(0))
+ ->method('config')
+ ->with('api.success')
+ ->will($this->returnValue($config));
+ $controller
+ ->expects($this->once())
+ ->method('set')
+ ->with('data', array());
+
+ $this->setReflectionClassInstance($listener);
+ $result = $this->callProtectedMethod('_ensureData', array($subject), $listener);
+ }
+
+/**
+ * test_ensureDataSubject
+ *
+ * @return void
+ */
+ public function test_ensureDataSubject() {
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('_controller', '_action'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('set'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $action = $this
+ ->getMockBuilder('CrudAction')
+ ->setMethods(array('config'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subject = new CrudSubject(array('success' => true, 'id' => 1, 'modelClass' => 'MyModel'));
+
+ $config = array('data' => array(
+ 'subject' => array(
+ '{modelClass}.id' => 'id',
+ 'modelClass'
+ )
+ ));
+
+ $i = 0;
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+ $listener
+ ->expects($this->at($i++))
+ ->method('_action')
+ ->will($this->returnValue($action));
+ $action
+ ->expects($this->at(0))
+ ->method('config')
+ ->with('api.success')
+ ->will($this->returnValue($config));
+ $controller
+ ->expects($this->once())
+ ->method('set')
+ ->with('data', array('modelClass' => 'MyModel', 'MyModel' => array('id' => 1)));
+
+ $this->setReflectionClassInstance($listener);
+ $result = $this->callProtectedMethod('_ensureData', array($subject), $listener);
+ }
+
+/**
+ * test_ensureDataRaw
+ *
+ * @return void
+ */
+ public function test_ensureDataRaw() {
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('_controller', '_action'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('set'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $action = $this
+ ->getMockBuilder('CrudAction')
+ ->setMethods(array('config'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subject = new CrudSubject(array('success' => true, 'id' => 1, 'modelClass' => 'MyModel'));
+
+ $config = array('data' => array('raw' => array('{modelClass}.id' => 1)));
+
+ $i = 0;
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+ $listener
+ ->expects($this->at($i++))
+ ->method('_action')
+ ->will($this->returnValue($action));
+ $action
+ ->expects($this->at(0))
+ ->method('config')
+ ->with('api.success')
+ ->will($this->returnValue($config));
+ $controller
+ ->expects($this->once())
+ ->method('set')
+ ->with('data', array('MyModel' => array('id' => 1)));
+
+ $this->setReflectionClassInstance($listener);
+ $result = $this->callProtectedMethod('_ensureData', array($subject), $listener);
+ }
+
+/**
+ * test_ensureDataError
+ *
+ * @return void
+ */
+ public function test_ensureDataError() {
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('_controller', '_action'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('set'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $action = $this
+ ->getMockBuilder('CrudAction')
+ ->setMethods(array('config'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subject = new CrudSubject(array('success' => false));
+
+ $config = array();
+
+ $i = 0;
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+ $listener
+ ->expects($this->at($i++))
+ ->method('_action')
+ ->will($this->returnValue($action));
+ $action
+ ->expects($this->at(0))
+ ->method('config')
+ ->with('api.error')
+ ->will($this->returnValue($config));
+ $controller
+ ->expects($this->once())
+ ->method('set')
+ ->with('data', array());
+
+ $this->setReflectionClassInstance($listener);
+ $result = $this->callProtectedMethod('_ensureData', array($subject), $listener);
+ }
+
+/**
+ * test_ensureDataExists
+ *
+ * @return void
+ */
+ public function test_ensureDataExists() {
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('_controller', '_action'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('set'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $controller->viewVars['data'] = true;
+
+ $subject = new CrudSubject();
+
+ $config = array();
+
+ $i = 0;
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+ $listener
+ ->expects($this->never())
+ ->method('_action');
+ $controller
+ ->expects($this->never())
+ ->method('set');
+
+ $this->setReflectionClassInstance($listener);
+ $result = $this->callProtectedMethod('_ensureData', array($subject), $listener);
+ }
+
+/**
+ * test_ensureSuccessAlreadySet
+ *
+ * @return void
+ */
+ public function test_ensureSuccessAlreadySet() {
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('_controller'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subject = new CrudSubject(array('success' => true));
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('set'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $controller->viewVars['success'] = true;
+
+ $i = 0;
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+ $controller
+ ->expects($this->never())
+ ->method('set');
+
+ $this->setReflectionClassInstance($listener);
+ $this->callProtectedMethod('_ensureSuccess', array($subject), $listener);
+ }
+
+/**
+ * testFlashMessageSupressed
+ *
+ * The API listener should suppress flash messages
+ * if the request is "API"
+ *
+ * @return void
+ */
+ public function testFlashMessageSupressed() {
+ $Request = new CakeRequest();
+ $Request->addDetector('api', array('callback' => function() {
+ return true;
+ }));
+
+ $subject = new CrudSubject(array('request' => $Request));
+
+ $apiListener = new ApiListener($subject);
+
+ $event = new CakeEvent('Crud.setFlash', $subject);
+ $apiListener->setFlash($event);
+
+ $stopped = $event->isStopped();
+ $this->assertTrue($stopped, 'Set flash event is expected to be stopped');
+ }
+
+/**
+ * Data provider for test_expandPath
+ *
+ * @return array
+ */
+ public function data_expandPath() {
+ return array(
+ 'simple string' => array(
+ new CrudSubject(array('modelClass' => 'MyModel')),
+ '{modelClass}.id',
+ 'MyModel.id'
+ ),
+
+ 'string and integer' => array(
+ new CrudSubject(array('modelClass' => 'MyModel', 'id' => 1)),
+ '{modelClass}.{id}',
+ 'MyModel.1'
+ ),
+
+ 'ignore non scalar' => array(
+ new CrudSubject(array('modelClass' => 'MyModel', 'complex' => new StdClass)),
+ '{modelClass}.{id}',
+ 'MyModel.{id}'
+ ),
+ );
+ }
+
+/**
+ * test_expandPath
+ *
+ * @dataProvider data_expandPath
+ * @return void
+ */
+ public function test_expandPath($subject, $path, $expected) {
+ $listener = new ApiListener(new CrudSubject());
+
+ $this->setReflectionClassInstance($listener);
+ $result = $this->callProtectedMethod('_expandPath', array($subject, $path), $listener);
+ $this->assertSame($expected, $result);
+ }
+
+/**
+ * testSetupDetectors
+ *
+ * @return void
+ */
+ public function testSetupDetectors() {
+ $detectors = array('xml' => array(), 'json' => array());
+
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('_request', 'config'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request = $this
+ ->getMockBuilder('CakeRequest')
+ ->setMethods(array('addDetector'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $i = 0;
+ $listener
+ ->expects($this->at($i++))
+ ->method('_request')
+ ->will($this->returnValue($request));
+ $listener
+ ->expects($this->at($i++))
+ ->method('config')
+ ->with('detectors')
+ ->will($this->returnValue($detectors));
+
+ $r = 0;
+ foreach ($detectors as $name => $config) {
+ $request
+ ->expects($this->at($r++))
+ ->method('addDetector')
+ ->with($name);
+ }
+
+ $request
+ ->expects($this->at($r++))
+ ->method('addDetector')
+ ->with('api');
+
+ $listener->setupDetectors();
+ }
+
+/**
+ * testSetupDetectorsIntigration
+ *
+ * @return void
+ */
+ public function testSetupDetectorsIntigration() {
+ $detectors = array(
+ 'json' => array('ext' => 'json', 'accepts' => 'application/json'),
+ 'xml' => array('ext' => 'xml', 'accepts' => 'text/xml')
+ );
+
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('_request', 'config'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request = $this
+ ->getMockBuilder('CakeRequest')
+ ->setMethods(array('accepts'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $i = 0;
+ $listener
+ ->expects($this->at($i++))
+ ->method('_request')
+ ->will($this->returnValue($request));
+ $listener
+ ->expects($this->at($i++))
+ ->method('config')
+ ->with('detectors')
+ ->will($this->returnValue($detectors));
+
+ $listener->setupDetectors();
+
+ // Test with "ext"
+ foreach ($detectors as $name => $configuration) {
+ $request->params['ext'] = $configuration['ext'];
+ $this->assertTrue($request->is($name));
+ }
+
+ $request->params['ext'] = null;
+
+ // Test with "accepts"
+ $r = 0;
+ foreach ($detectors as $name => $configuration) {
+ $request
+ ->expects($this->at($r++))
+ ->method('accepts')
+ ->with($configuration['accepts'])
+ ->will($this->returnValue(true));
+ }
+
+ foreach ($detectors as $name => $config) {
+ $this->assertTrue($request->is($name));
+ }
+
+ $request->params['ext'] = 'xml';
+ $this->assertTrue($request->is('api'));
+
+ $request->params['ext'] = null;
+ $this->assertFalse($request->is('api'));
+ }
+
+/**
+ * testRegisterExceptionHandler with Api request
+ *
+ * @return void
+ */
+ public function testRegisterExceptionHandlerWithApi() {
+ $listener = $this->getMockBuilder('ApiListener')
+ ->setMethods(array('_request'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request = $this->getMockBuilder('CakeRequest')
+ ->setMethods(array('is'))
+ ->disableOriginalConstructor()
+ ->getMock();
+ $request
+ ->expects($this->at(0))
+ ->method('is')
+ ->with('api')
+ ->will($this->returnValue(true));
+
+ $listener
+ ->expects($this->once())
+ ->method('_request')
+ ->with()
+ ->will($this->returnValue($request));
+
+ $listener->registerExceptionHandler();
+
+ $expected = 'Crud.CrudExceptionRenderer';
+ $result = Configure::read('Exception.renderer');
+ $this->assertEquals($expected, $result);
+ }
+
+
+/**
+ * testRegisterExceptionHandler without Api request
+ *
+ * @return void
+ */
+ public function testRegisterExceptionHandlerWithoutApi() {
+ $listener = $this->getMockBuilder('ApiListener')
+ ->setMethods(array('_request'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request = $this->getMockBuilder('CakeRequest')
+ ->setMethods(array('is'))
+ ->disableOriginalConstructor()
+ ->getMock();
+ $request
+ ->expects($this->at(0))
+ ->method('is')
+ ->with('api')
+ ->will($this->returnValue(false));
+
+ $listener
+ ->expects($this->once())
+ ->method('_request')
+ ->with()
+ ->will($this->returnValue($request));
+
+ $listener->registerExceptionHandler();
+
+ $expected = 'ExceptionRenderer';
+ $result = Configure::read('Exception.renderer');
+ $this->assertEquals($expected, $result);
+ }
+/**
+ * data provider for test_checkRequestMethods
+ *
+ * @return array
+ */
+ public function data_checkRequestMethods() {
+ return array(
+ 'defaults' => array(
+ array(),
+ false,
+ array()
+ ),
+ 'valid get' => array(
+ array('methods' => array('get')),
+ true,
+ array('get' => true)
+ ),
+ 'invalid post' => array(
+ array('methods' => array('post')),
+ 'BadRequestException',
+ array('post' => false)
+ ),
+ 'valid put' => array(
+ array('methods' => array('post', 'get', 'put')),
+ true,
+ array('post' => false, 'get' => false, 'put' => true)
+ )
+ );
+ }
+
+/**
+ * test_checkRequestMethods
+ *
+ * @dataProvider data_checkRequestMethods
+ * @return void
+ */
+ public function test_checkRequestMethods($apiConfig, $exception, $requestMethods) {
+ $listener = $this
+ ->getMockBuilder('ApiListener')
+ ->setMethods(array('_action', '_request'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $action = $this
+ ->getMockBuilder('CrudAction')
+ ->setMethods(array('config'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request = $this
+ ->getMockBuilder('CakeRequest')
+ ->setMethods(array('is'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $listener
+ ->expects($this->at(0))
+ ->method('_action')
+ ->will($this->returnValue($action));
+ $action
+ ->expects($this->at(0))
+ ->method('config')
+ ->with('api')
+ ->will($this->returnValue($apiConfig));
+
+ if (!empty($apiConfig['methods'])) {
+ $listener
+ ->expects($this->at(1))
+ ->method('_request')
+ ->will($this->returnValue($request));
+
+ $r = 0;
+ foreach ($requestMethods as $method => $bool) {
+ $request
+ ->expects($this->at($r++))
+ ->method('is')
+ ->with($method)
+ ->will($this->returnValue($bool));
+ }
+ } else {
+ $listener
+ ->expects($this->never())
+ ->method('_request');
+ }
+
+ if (is_string($exception)) {
+ $this->expectException($exception);
+ }
+
+ $this->setReflectionClassInstance($listener);
+ $result = $this->callProtectedMethod('_checkRequestMethods', array(), $listener);
+
+ if (is_bool($exception)) {
+ $this->assertEquals($exception, $result);
+ }
+ }
+
+/**
+ * testMapResources
+ *
+ * Passing no argument, should map all of the app's controllers
+ *
+ * @return void
+ */
+ public function testMapResources() {
+ $path = CAKE . 'Test' . DS . 'test_app' . DS . 'Controller' . DS;
+ App::build(array(
+ 'Controller' => array($path)
+ ), App::RESET);
+
+ Router::reload();
+ Router::$routes = array();
+
+ ApiListener::mapResources();
+
+ $expected = $this->_getRouteExpectations();
+ $return = $this->_currentRoutes();
+
+ $this->assertSame($expected, $return, 'test_app contains several controllers - there should be rest routes for all of them');
+ }
+
+/**
+ * _getRouteExpectations
+ *
+ * A little helper function which returns routes expectations for all app controllers
+ *
+ * @return array
+ */
+ protected function _getRouteExpectations() {
+ $routePatterns = array(
+ 'GET index /{name}',
+ 'GET view /{name}/:id',
+ 'POST add /{name}',
+ 'PUT edit /{name}/:id',
+ 'DELETE delete /{name}/:id',
+ 'POST edit /{name}/:id',
+ );
+
+ $expected = array();
+ $controllers = App::objects('Controller');
+ foreach ($controllers as $controller) {
+ $controller = substr($controller, 0, - strlen('Controller'));
+ $controller = Inflector::underscore($controller);
+ if ($controller === 'app') {
+ continue;
+ }
+
+ $routes = $routePatterns;
+ foreach ($routes as &$route) {
+ $route = str_replace('{name}', $controller, $route);
+ }
+
+ $expected = array_merge($expected, $routes);
+ }
+
+ return $expected;
+ }
+
+/**
+ * Passing a plugin name should map only for that plugin
+ *
+ * @return void
+ */
+ public function testMapResourcesPlugin() {
+ $path = CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS;
+ App::build(array(
+ 'Plugin' => array($path)
+ ), App::RESET);
+ CakePlugin::load('TestPlugin');
+
+ Router::reload();
+ Router::$routes = array();
+
+ ApiListener::mapResources('TestPlugin');
+
+ $expected = array(
+ 'GET index /test_plugin/test_plugin',
+ 'GET view /test_plugin/test_plugin/:id',
+ 'POST add /test_plugin/test_plugin',
+ 'PUT edit /test_plugin/test_plugin/:id',
+ 'DELETE delete /test_plugin/test_plugin/:id',
+ 'POST edit /test_plugin/test_plugin/:id',
+ 'GET index /test_plugin/tests',
+ 'GET view /test_plugin/tests/:id',
+ 'POST add /test_plugin/tests',
+ 'PUT edit /test_plugin/tests/:id',
+ 'DELETE delete /test_plugin/tests/:id',
+ 'POST edit /test_plugin/tests/:id',
+ );
+ $return = $this->_currentRoutes();
+
+ $this->assertSame($expected, $return, 'test plugin contains a test plugin and tests controller');
+ }
+
+/**
+ * _currentRoutes
+ *
+ * Return current route definitions in a very simple format for comparison purposes
+ *
+ * @return array
+ */
+ protected function _currentRoutes() {
+ $return = array();
+
+ foreach (Router::$routes as $route) {
+ $return[] = $route->defaults['[method]'] .
+ ' ' . $route->defaults['action'] .
+ ' ' . $route->template;
+ }
+
+ return $return;
+ }
+
+/**
+ * testViewClass
+ *
+ * Test that both set and get works
+ *
+ * @return void
+ */
+ public function testViewClass() {
+ $apiListener = new ApiListener(new CrudSubject());
+
+ $result = $apiListener->viewClass('json', 'Sample.ViewClass');
+ $this->assertEquals($apiListener, $result, 'Setting a viewClass did not return the listener itself');
+
+ $result = $apiListener->viewClass('json');
+ $this->assertEquals('Sample.ViewClass', $result, 'The changed viewClass was not returned');
+ }
+
+/**
+ * testViewClassDefaults
+ *
+ * Test that the default viewClasses are as expected
+ *
+ * @return void
+ */
+ public function testViewClassDefaults() {
+ $apiListener = new ApiListener(new CrudSubject());
+
+ $result = $apiListener->config('viewClasses');
+ $expected = array(
+ 'json' => 'Crud.CrudJson',
+ 'xml' => 'Crud.CrudXml'
+ );
+ $this->assertEquals($expected, $result, 'The default viewClasses setting has changed');
+ }
+
+/**
+ * testInjectViewClasses
+ *
+ * @return void
+ */
+ public function testInjectViewClasses() {
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('foo')) // need to mock *something* to make Controller::__set work
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $controller->RequestHandler = $this->getMock('RequestHandler', array('viewClassMap'));
+ $controller->RequestHandler->expects($this->at(0))->method('viewClassMap')->with('json', 'Crud.CrudJson');
+ $controller->RequestHandler->expects($this->at(1))->method('viewClassMap')->with('xml', 'Crud.CrudXml');
+
+ $apiListener = $this->getMock('ApiListener', array('_controller'), array(new CrudSubject()));
+ $apiListener->expects($this->once())->method('_controller')->will($this->returnValue($controller));
+ $apiListener->injectViewClasses();
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiPaginationListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiPaginationListenerTest.php
new file mode 100644
index 000000000..4bf1cc658
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiPaginationListenerTest.php
@@ -0,0 +1,167 @@
+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));
+ }
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiQueryLogListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiQueryLogListenerTest.php
new file mode 100644
index 000000000..2a96f4e20
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiQueryLogListenerTest.php
@@ -0,0 +1,196 @@
+_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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiTransformationListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiTransformationListenerTest.php
new file mode 100644
index 000000000..902fe22bc
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiTransformationListenerTest.php
@@ -0,0 +1,1441 @@
+getMockBuilder('ApiTransformationListener')
+ ->setMethods(array('_request'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $expected = array('Crud.beforeRender' => array('callable' => 'beforeRender', 'priority' => 200));
+ $result = $listener->implementedEvents();
+ $this->assertSame($expected, $result);
+ }
+
+/**
+ * testBeforeRenderNoApiRequest
+ *
+ * @return void
+ */
+ public function testBeforeRenderNoApiRequest() {
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->setMethods(array('_request'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request = $this
+ ->getMockBuilder('CakeRequest')
+ ->setMethods(array('is'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request
+ ->expects($this->once())
+ ->method('is')
+ ->with('api')
+ ->will($this->returnValue(false));
+
+ $listener
+ ->expects($this->once())
+ ->method('_request')
+ ->will($this->returnValue($request));
+
+ $listener
+ ->expects($this->never())
+ ->method('_controller');
+
+ $listener
+ ->expects($this->never())
+ ->method('_action');
+
+ $listener
+ ->expects($this->never())
+ ->method('_model');
+
+ $listener
+ ->expects($this->never())
+ ->method('_setMethods');
+
+ $listener
+ ->expects($this->never())
+ ->method('_changeNesting');
+
+ $listener
+ ->expects($this->never())
+ ->method('_recurse');
+
+ $this->assertTrue($listener->beforeRender());
+ }
+
+/**
+ * testBeforeRenderWithNestingChange
+ *
+ * @return void
+ */
+ public function testBeforeRenderWithNestingChange() {
+ $i = 0;
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->setMethods(array('_request', '_controller', '_action', '_model', '_changeNesting', '_recurse', '_setMethods'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request = $this
+ ->getMockBuilder('CakeRequest')
+ ->setMethods(array('is'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request
+ ->expects($this->once())
+ ->method('is')
+ ->with('api')
+ ->will($this->returnValue(true));
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('set'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $action = $this
+ ->getMockBuilder('IndexAction')
+ ->setMethods(array('viewVar'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $model = $this
+ ->getMockBuilder('Model')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_request')
+ ->will($this->returnValue($request));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_action')
+ ->will($this->returnValue($action));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_setMethods');
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_model')
+ ->will($this->returnValue($model));
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => true,
+ 'changeTime' => true,
+ 'castNumbers' => true,
+ 'keyMethods' => array(),
+ 'valueMethods' => array(),
+ 'replaceMap' => array()
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $success = true;
+ $users = array(
+ 0 => array(
+ 'User' => array('id' => 5, 'name' => 'FriendsOfCake'),
+ 'Profile' => array('id' => 987, 'twitter' => '@FriendsOfCake')
+ ),
+ 1 => array(
+ 'User' => array('id' => 45, 'name' => 'CakePHP'),
+ 'Profile' => array('id' => 123, 'twitter' => '@cakephp')
+ ),
+ );
+
+ $controller->viewVars = compact('success', 'users');
+
+ $action
+ ->expects($this->once())
+ ->method('viewVar')
+ ->will($this->returnValue('users'));
+
+ $model->alias = 'User';
+
+ $result = array();
+
+ $nested = array(
+ 'id' => 5,
+ 'name' => 'FriendsOfCake',
+ 'Profile' => array('id' => 987, 'twitter' => '@FriendsOfCake')
+ );
+ $result[] = $nested;
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_changeNesting')
+ ->with($this->identicalTo($users[0]), 'User')
+ ->will($this->returnValue($nested));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_recurse')
+ ->with($nested);
+
+ $nested = array(
+ 'id' => 45,
+ 'name' => 'CakePHP',
+ 'Profile' => array('id' => 123, 'twitter' => '@cakephp')
+ );
+ $result[] = $nested;
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_changeNesting')
+ ->with($this->identicalTo($users[1]), 'User')
+ ->will($this->returnValue($nested));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_recurse')
+ ->with($nested);
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+
+ $controller
+ ->expects($this->once())
+ ->method('set')
+ ->with('users', $result);
+
+ $this->assertTrue($listener->beforeRender());
+ }
+
+/**
+ * testBeforeRenderWithoutNestingChange
+ *
+ * @return void
+ */
+ public function testBeforeRenderWithoutNestingChange() {
+ $i = 0;
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->setMethods(array('_request', '_controller', '_action', '_model', '_changeNesting', '_recurse', '_setMethods'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request = $this
+ ->getMockBuilder('CakeRequest')
+ ->setMethods(array('is'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request
+ ->expects($this->once())
+ ->method('is')
+ ->with('api')
+ ->will($this->returnValue(true));
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('set'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $action = $this
+ ->getMockBuilder('IndexAction')
+ ->setMethods(array('viewVar'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $model = $this
+ ->getMockBuilder('Model')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_request')
+ ->will($this->returnValue($request));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_action')
+ ->will($this->returnValue($action));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_setMethods');
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_model')
+ ->will($this->returnValue($model));
+
+ $settings = array(
+ 'changeNesting' => false,
+ 'changeKeys' => true,
+ 'changeTime' => true,
+ 'castNumbers' => true,
+ 'keyMethods' => array(),
+ 'valueMethods' => array(),
+ 'replaceMap' => array()
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $success = true;
+ $users = array(
+ 0 => array(
+ 'User' => array('id' => 5, 'name' => 'FriendsOfCake'),
+ 'Profile' => array('id' => 987, 'twitter' => '@FriendsOfCake')
+ ),
+ 1 => array(
+ 'User' => array('id' => 45, 'name' => 'CakePHP'),
+ 'Profile' => array('id' => 123, 'twitter' => '@cakephp')
+ )
+ );
+
+ $controller->viewVars = compact('success', 'users');
+
+ $action
+ ->expects($this->once())
+ ->method('viewVar')
+ ->will($this->returnValue('users'));
+
+ $model->alias = 'User';
+
+ $listener
+ ->expects($this->never())
+ ->method('_changeNesting');
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_recurse')
+ ->with($this->identicalTo($users[0]));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_recurse')
+ ->with($this->identicalTo($users[1]));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+
+ $controller
+ ->expects($this->once())
+ ->method('set')
+ ->with('users', $users);
+
+ $this->assertTrue($listener->beforeRender());
+ }
+
+/**
+ * testBeforeRenderWithFindFirstAndNestingChange
+ *
+ * @return void
+ */
+ public function testBeforeRenderWithFindFirstAndNestingChange() {
+ $i = 0;
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->setMethods(array('_request', '_controller', '_action', '_model', '_changeNesting', '_recurse', '_setMethods'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request = $this
+ ->getMockBuilder('CakeRequest')
+ ->setMethods(array('is'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request
+ ->expects($this->once())
+ ->method('is')
+ ->with('api')
+ ->will($this->returnValue(true));
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('set'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $action = $this
+ ->getMockBuilder('IndexAction')
+ ->setMethods(array('viewVar'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $model = $this
+ ->getMockBuilder('Model')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_request')
+ ->will($this->returnValue($request));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_action')
+ ->will($this->returnValue($action));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_setMethods');
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_model')
+ ->will($this->returnValue($model));
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => true,
+ 'changeTime' => true,
+ 'castNumbers' => true,
+ 'keyMethods' => array(),
+ 'valueMethods' => array(),
+ 'replaceMap' => array()
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $success = true;
+ $user = array(
+ 'User' => array(
+ 'id' => 5,
+ 'name' => 'FriendsOfCake'
+ ),
+ 'Profile' => array(
+ 'id' => 987,
+ 'twitter' => '@FriendsOfCake'
+ )
+ );
+
+ $controller->viewVars = compact('success', 'user');
+
+ $action
+ ->expects($this->once())
+ ->method('viewVar')
+ ->will($this->returnValue('user'));
+
+ $model->alias = 'User';
+
+ $nested = array(
+ 'id' => 5,
+ 'name' => 'FriendsOfCake',
+ 'Profile' => array('id' => 987, 'twitter' => '@FriendsOfCake')
+ );
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_changeNesting')
+ ->with($this->identicalTo($user), 'User')
+ ->will($this->returnValue($nested));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_recurse')
+ ->with($nested);
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+
+ $controller
+ ->expects($this->once())
+ ->method('set')
+ ->with('user', $nested);
+
+ $this->assertTrue($listener->beforeRender());
+ }
+
+/**
+ * testBeforeRenderWithoutViewVar
+ *
+ * @return void
+ */
+ public function testBeforeRenderWithoutViewVar() {
+ $i = 0;
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->setMethods(array('_request', '_controller', '_action', '_model', '_changeNesting', '_recurse', '_setMethods'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request = $this
+ ->getMockBuilder('CakeRequest')
+ ->setMethods(array('is'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request
+ ->expects($this->once())
+ ->method('is')
+ ->with('api')
+ ->will($this->returnValue(true));
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('set'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $action = $this
+ ->getMockBuilder('IndexAction')
+ ->setMethods(array('viewVar'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $model = $this
+ ->getMockBuilder('Model')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_request')
+ ->will($this->returnValue($request));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_action')
+ ->will($this->returnValue($action));
+
+ $listener
+ ->expects($this->never())
+ ->method('_model')
+ ->will($this->returnValue($model));
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => true,
+ 'changeTime' => true,
+ 'castNumbers' => true,
+ 'keyMethods' => array(),
+ 'valueMethods' => array(),
+ 'replaceMap' => array()
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $success = true;
+
+ $controller->viewVars = compact('success');
+
+ $action
+ ->expects($this->once())
+ ->method('viewVar')
+ ->will($this->returnValue('users'));
+
+ $model->alias = 'User';
+
+ $listener
+ ->expects($this->never())
+ ->method('_changeNesting');
+
+ $listener
+ ->expects($this->never())
+ ->method('_recurse');
+
+ $controller
+ ->expects($this->never())
+ ->method('set');
+
+ $this->assertTrue($listener->beforeRender());
+ }
+
+/**
+ * testBeforeRenderWithEmptyViewVar
+ *
+ * @return void
+ */
+ public function testBeforeRenderWithEmptyViewVar() {
+ $i = 0;
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->setMethods(array('_request', '_controller', '_action', '_model', '_changeNesting', '_recurse', '_setMethods'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request = $this
+ ->getMockBuilder('CakeRequest')
+ ->setMethods(array('is'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request
+ ->expects($this->once())
+ ->method('is')
+ ->with('api')
+ ->will($this->returnValue(true));
+
+ $controller = $this
+ ->getMockBuilder('Controller')
+ ->setMethods(array('set'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $action = $this
+ ->getMockBuilder('IndexAction')
+ ->setMethods(array('viewVar'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $model = $this
+ ->getMockBuilder('Model')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_request')
+ ->will($this->returnValue($request));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_controller')
+ ->will($this->returnValue($controller));
+
+ $listener
+ ->expects($this->at($i++))
+ ->method('_action')
+ ->will($this->returnValue($action));
+
+ $listener
+ ->expects($this->never())
+ ->method('_model')
+ ->will($this->returnValue($model));
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => true,
+ 'changeTime' => true,
+ 'castNumbers' => true,
+ 'keyMethods' => array(),
+ 'valueMethods' => array(),
+ 'replaceMap' => array()
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $success = true;
+ $users = array();
+
+ $controller->viewVars = compact('success', 'users');
+
+ $action
+ ->expects($this->once())
+ ->method('viewVar')
+ ->will($this->returnValue('users'));
+
+ $model->alias = 'User';
+
+ $listener
+ ->expects($this->never())
+ ->method('_changeNesting');
+
+ $listener
+ ->expects($this->never())
+ ->method('_recurse');
+
+ $controller
+ ->expects($this->never())
+ ->method('set');
+
+ $this->assertTrue($listener->beforeRender());
+ }
+
+/**
+ * testSetMethods
+ *
+ * @return void
+ */
+ public function testSetMethods() {
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->setReflectionClassInstance($listener);
+
+ $closure = function($variable) {
+ return $variable;
+ };
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => true,
+ 'changeTime' => true,
+ 'castNumbers' => true,
+ 'keyMethods' => array(),
+ 'valueMethods' => array($closure),
+ 'replaceMap' => array()
+ );
+
+ $this->setProtectedProperty('_settings', $settings, $listener);
+ $this->callProtectedMethod('_setMethods', array(), $listener);
+ $result = $this->getProtectedProperty('_settings', $listener);
+
+ $expected = array('_replaceKeys');
+ $this->assertSame($expected, $result['keyMethods']);
+
+ $expected = array('_castNumbers', '_changeDateToUnix', $closure);
+ $this->assertSame($expected, $result['valueMethods']);
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => false,
+ 'changeTime' => false,
+ 'castNumbers' => true,
+ 'keyMethods' => array(),
+ 'valueMethods' => array($closure),
+ 'replaceMap' => array()
+ );
+
+ $this->setProtectedProperty('_settings', $settings, $listener);
+ $this->callProtectedMethod('_setMethods', array(), $listener);
+ $result = $this->getProtectedProperty('_settings', $listener);
+
+ $expected = array();
+ $this->assertSame($expected, $result['keyMethods']);
+
+ $expected = array('_castNumbers', $closure);
+ $this->assertSame($expected, $result['valueMethods']);
+ }
+
+/**
+ * testRecurseWithKeysAndCasts
+ *
+ * @return void
+ */
+ public function testRecurseWithKeysAndCasts() {
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => true,
+ 'changeTime' => true,
+ 'castNumbers' => true,
+ 'keyMethods' => array('_replaceKeys'),
+ 'valueMethods' => array('_castNumbers', '_changeDateToUnix'),
+ 'replaceMap' => array('User' => 'user', 'Profile' => 'profile')
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $data = array(
+ 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
+ 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
+ );
+
+ $expected = array(
+ 'user' => array('id' => 5, 'name' => 'FriendsOfCake'),
+ 'profile' => array('id' => 987, 'twitter' => '@FriendsOfCake')
+ );
+
+ $this->callProtectedMethod('_recurse', array(&$data), $listener);
+
+ $this->assertSame($expected, $data);
+ }
+
+/**
+ * testRecurseNoCasts
+ *
+ * @return void
+ */
+ public function testRecurseNoCasts() {
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => true,
+ 'changeTime' => false,
+ 'castNumbers' => false,
+ 'keyMethods' => array('_replaceKeys'),
+ 'valueMethods' => array(),
+ 'replaceMap' => array('User' => 'user', 'Profile' => 'profile')
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $data = array(
+ 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
+ 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
+ );
+
+ $expected = array(
+ 'user' => array('id' => '5', 'name' => 'FriendsOfCake'),
+ 'profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
+ );
+
+ $this->callProtectedMethod('_recurse', array(&$data), $listener);
+
+ $this->assertSame($expected, $data);
+ }
+
+/**
+ * testRecurseNoCastsMapWithoutMatch
+ *
+ * @return void
+ */
+ public function testRecurseNoCastsMapWithoutMatch() {
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => true,
+ 'changeTime' => false,
+ 'castNumbers' => false,
+ 'keyMethods' => array('_replaceKeys'),
+ 'valueMethods' => array(),
+ 'replaceMap' => array('CakePHP' => 'FriendsOfCake')
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $data = array(
+ 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
+ 'Comment' => array()
+ );
+
+ $expected = array(
+ 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
+ 'Comment' => array()
+ );
+
+ $this->callProtectedMethod('_recurse', array(&$data), $listener);
+
+ $this->assertSame($expected, $data);
+ }
+
+/**
+ * testRecurseNoCastsHasMany
+ *
+ * @return void
+ */
+ public function testRecurseNoCastsEmptyHasMany() {
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->setMethods(array('_model'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user = $this
+ ->getMockBuilder('Model')
+ ->disableOriginalConstructor()
+ ->setMethods(array('find'))
+ ->getMock();
+ $user->name = $user->alias = 'User';
+
+ $comment = $this
+ ->getMockBuilder('Model')
+ ->disableOriginalConstructor()
+ ->setMethods(array('find'))
+ ->getMock();
+ $comment->name = $comment->alias = 'Comment';
+
+ $user->hasMany = array('Comment' => array('className' => 'Comment', 'foreign_key' => 'user_id'));
+ $user->Comment = $comment;
+
+ $listener
+ ->expects($this->once())
+ ->method('_model')
+ ->will($this->returnValue($user));
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => true,
+ 'changeTime' => false,
+ 'castNumbers' => false,
+ 'keyMethods' => array('_replaceKeys'),
+ 'valueMethods' => array(),
+ 'replaceMap' => array()
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $data = array(
+ 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
+ 'Comment' => array()
+ );
+
+ $expected = array(
+ 'user' => array('id' => '5', 'name' => 'FriendsOfCake'),
+ 'comments' => array()
+ );
+
+ $this->callProtectedMethod('_recurse', array(&$data), $listener);
+
+ $this->assertSame($expected, $data);
+ }
+
+/**
+ * testRecurseNoKeys
+ *
+ * @return void
+ */
+ public function testRecurseNoKeys() {
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => false,
+ 'changeTime' => true,
+ 'castNumbers' => true,
+ 'keyMethods' => array(),
+ 'valueMethods' => array('_castNumbers', '_changeDateToUnix'),
+ 'replaceMap' => array()
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $data = array(
+ 'User' => array('id' => '5', 'name' => 'FriendsOfCake', 'created' => '2013-08-26 11:24:54'),
+ 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
+ );
+
+ $expected = array(
+ 'User' => array(
+ 'id' => 5,
+ 'name' => 'FriendsOfCake',
+ 'created' => strtotime('2013-08-26 11:24:54')
+ ),
+ 'Profile' => array(
+ 'id' => 987,
+ 'twitter' => '@FriendsOfCake'
+ )
+ );
+
+ $this->callProtectedMethod('_recurse', array(&$data), $listener);
+
+ $this->assertSame($expected, $data);
+ }
+
+/**
+ * testRecurseNoKeysAndNoCasts
+ *
+ * @return void
+ */
+ public function testRecurseNoKeysAndNoCasts() {
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => false,
+ 'changeTime' => false,
+ 'castNumbers' => false,
+ 'keyMethods' => array(),
+ 'valueMethods' => array(),
+ 'replaceMap' => array()
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $data = array(
+ 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
+ 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
+ );
+
+ $expected = $data;
+
+ $this->callProtectedMethod('_recurse', array(&$data), $listener);
+
+ $this->assertSame($expected, $data);
+ }
+
+/**
+ * testRecurseWithGlobalFunction
+ *
+ * @return void
+ */
+ public function testRecurseWithGlobalFunction() {
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => false,
+ 'changeTime' => false,
+ 'castNumbers' => false,
+ 'keyMethods' => array(),
+ 'valueMethods' => array(function($value, $key) {
+ return strtoupper($value);
+ }),
+ 'replaceMap' => array()
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $data = array(
+ 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
+ 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
+ );
+
+ $expected = array(
+ 'User' => array('id' => '5', 'name' => 'FRIENDSOFCAKE'),
+ 'Profile' => array('id' => '987', 'twitter' => '@FRIENDSOFCAKE')
+ );
+
+ $this->callProtectedMethod('_recurse', array(&$data), $listener);
+
+ $this->assertSame($expected, $data);
+ }
+
+/**
+ * testRecurseWithStaticMethod
+ *
+ * @return void
+ */
+ public function testRecurseWithStaticMethod() {
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => false,
+ 'changeTime' => false,
+ 'castNumbers' => false,
+ 'keyMethods' => array(),
+ 'valueMethods' => array('ApiTransformationListenerTest::staticExample'),
+ 'replaceMap' => array()
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $data = array(
+ 'User' => array('id' => '5', 'name' => 'Friends Of Cake'),
+ 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
+ );
+
+ $expected = array(
+ 'User' => array('id' => '5', 'name' => 'Friends_Of_Cake'),
+ 'Profile' => array('id' => '987', 'twitter' => 'FriendsOfCake')
+ );
+
+ $this->callProtectedMethod('_recurse', array(&$data), $listener);
+
+ $this->assertSame($expected, $data);
+ }
+
+/**
+ * testRecurseWithClosure
+ *
+ * @return void
+ */
+ public function testRecurseWithClosure() {
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $uppercase = function($variable) {
+ if (!is_string($variable)) {
+ return $variable;
+ }
+ return strtoupper($variable);
+ };
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => false,
+ 'changeTime' => false,
+ 'castNumbers' => false,
+ 'keyMethods' => array(),
+ 'valueMethods' => array($uppercase),
+ 'replaceMap' => array()
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $data = array(
+ 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
+ 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
+ );
+
+ $expected = array(
+ 'User' => array('id' => '5', 'name' => 'FRIENDSOFCAKE'),
+ 'Profile' => array('id' => '987', 'twitter' => '@FRIENDSOFCAKE')
+ );
+
+ $this->callProtectedMethod('_recurse', array(&$data), $listener);
+
+ $this->assertSame($expected, $data);
+ }
+
+ public function testRecurseKeySpecificTransform() {
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $callback = function($variable, $key) {
+ if ($key === 'User.changeme') {
+ return 'changed';
+ }
+ return $variable;
+ };
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => false,
+ 'changeTime' => false,
+ 'castNumbers' => false,
+ 'keyMethods' => array(),
+ 'valueMethods' => array($callback),
+ 'replaceMap' => array()
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $data = array(
+ 'User' => array(
+ 'id' => '5',
+ 'name' => 'FriendsOfCake',
+ 'changeme' => 'old'
+ ),
+ 'Profile' => array(
+ 'id' => '987',
+ 'twitter' => '@FriendsOfCake'
+ )
+ );
+
+ $expected = array(
+ 'User' => array(
+ 'id' => '5',
+ 'name' => 'FriendsOfCake',
+ 'changeme' => 'changed'
+ ),
+ 'Profile' => array(
+ 'id' => '987',
+ 'twitter' => '@FriendsOfCake'
+ )
+ );
+
+ $this->callProtectedMethod('_recurse', array(&$data), $listener);
+
+ $this->assertSame($expected, $data);
+ }
+
+ public function testRecurseKeySpecificNestedTransform() {
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $callback = function($variable, $key) {
+ if (preg_match('@^\d+.User\.changeme$@', $key)) {
+ return 'changed';
+ }
+ return $variable;
+ };
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => false,
+ 'changeTime' => false,
+ 'castNumbers' => false,
+ 'keyMethods' => array(),
+ 'valueMethods' => array($callback),
+ 'replaceMap' => array()
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $data = array(
+ 'changeme' => 'no change',
+ array(
+ 'changeme' => 'no change',
+ 'User' => array(
+ 'id' => '5',
+ 'name' => 'FriendsOfCake',
+ 'changeme' => 'old'
+ ),
+ 'Profile' => array(
+ 'id' => '987',
+ 'twitter' => '@FriendsOfCake'
+ )
+ ),
+ array(
+ 'User' => array(
+ 'id' => '6',
+ 'name' => 'FriendsOfCake',
+ 'changeme' => 'old two'
+ ),
+ 'Profile' => array(
+ 'id' => '987',
+ 'twitter' => '@FriendsOfCake'
+ )
+ )
+ );
+
+ $expected = array(
+ 'changeme' => 'no change',
+ array(
+ 'changeme' => 'no change',
+ 'User' => array(
+ 'id' => '5',
+ 'name' => 'FriendsOfCake',
+ 'changeme' => 'changed'
+ ),
+ 'Profile' => array(
+ 'id' => '987',
+ 'twitter' => '@FriendsOfCake'
+ )
+ ),
+ array(
+ 'User' => array(
+ 'id' => '6',
+ 'name' => 'FriendsOfCake',
+ 'changeme' => 'changed'
+ ),
+ 'Profile' => array(
+ 'id' => '987',
+ 'twitter' => '@FriendsOfCake'
+ )
+ )
+ );
+
+ $this->callProtectedMethod('_recurse', array(&$data), $listener);
+
+ $this->assertSame($expected, $data);
+ }
+
+/**
+ * testGetReplaceMapFromAssociationsEndlessLoopPrevention
+ *
+ * @return void
+ */
+ public function testGetReplaceMapFromAssociationsEndlessLoopPrevention() {
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->setMethods(array('_model'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user = $this
+ ->getMockBuilder('Model')
+ ->disableOriginalConstructor()
+ ->setMethods(array('find'))
+ ->getMock();
+ $user->name = $user->alias = 'User';
+
+ $user->belongsTo = array('User' => array('className' => 'User', 'foreignKey' => 'user_id'));
+ $user->User = $user;
+
+ $listener
+ ->expects($this->once())
+ ->method('_model')
+ ->will($this->returnValue($user));
+
+ $this->setReflectionClassInstance($listener);
+
+ $expected = array('User' => 'user');
+ $result = $this->callProtectedMethod('_getReplaceMapFromAssociations', array(), $listener);
+
+ $this->assertSame($expected, $result);
+ }
+
+/**
+ * testGetReplaceMapFromAssociationsDeepSingleRecordAssociations
+ *
+ * @return void
+ */
+ public function testGetReplaceMapFromAssociationsDeepSingleRecordAssociations() {
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->setMethods(array('_model'))
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user = $this
+ ->getMockBuilder('Model')
+ ->disableOriginalConstructor()
+ ->setMethods(array('find'))
+ ->getMock();
+ $user->name = $user->alias = 'User';
+
+ $group = $this
+ ->getMockBuilder('Model')
+ ->disableOriginalConstructor()
+ ->setMethods(array('find'))
+ ->getMock();
+ $group->name = $group->alias = 'Group';
+
+ $ambassador = $this
+ ->getMockBuilder('Model')
+ ->disableOriginalConstructor()
+ ->setMethods(array('find'))
+ ->getMock();
+ $ambassador->name = $ambassador->alias = 'Ambassador';
+
+ $user->belongsTo = array('Group' => array('className' => 'Group', 'foreignKey' => 'group_id'));
+ $group->hasOne = array('Ambassador' => array('className' => 'Ambassador', 'foreignKey' => 'group_id'));
+
+ $group->Ambassador = $ambassador;
+ $user->Group = $group;
+
+ $listener
+ ->expects($this->once())
+ ->method('_model')
+ ->will($this->returnValue($user));
+
+ $this->setReflectionClassInstance($listener);
+
+ $expected = array('User' => 'user', 'Group' => 'group', 'Ambassador' => 'ambassador');
+ $result = $this->callProtectedMethod('_getReplaceMapFromAssociations', array(), $listener);
+
+ $this->assertSame($expected, $result);
+ }
+
+/**
+ * testChangeNesting
+ *
+ * @return void
+ */
+ public function testChangeNesting() {
+ $listener = $this
+ ->getMockBuilder('ApiTransformationListener')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $settings = array(
+ 'changeNesting' => true,
+ 'changeKeys' => false,
+ 'changeTime' => false,
+ 'castNumbers' => false,
+ 'keyMethods' => array(),
+ 'valueMethods' => array(),
+ 'replaceMap' => array()
+ );
+
+ $this->setReflectionClassInstance($listener);
+ $this->setProtectedProperty('_settings', $settings, $listener);
+
+ $data = array(
+ 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
+ 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
+ );
+
+ $expected = array(
+ 'id' => '5',
+ 'name' => 'FriendsOfCake',
+ 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
+ );
+
+ $result = $this->callProtectedMethod('_changeNesting', array(&$data, 'User'), $listener);
+
+ $this->assertSame($expected, $result);
+
+ $data = array(
+ 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
+ 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
+ );
+
+ $expected = array(
+ 'id' => '987',
+ 'twitter' => '@FriendsOfCake',
+ 'User' => array(
+ 'id' => '5',
+ 'name' => 'FriendsOfCake',
+ )
+ );
+
+ $result = $this->callProtectedMethod('_changeNesting', array(&$data, 'Profile'), $listener);
+
+ $this->assertSame($expected, $result);
+ }
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/RedirectListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/RedirectListenerTest.php
new file mode 100644
index 000000000..c25e213ca
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/RedirectListenerTest.php
@@ -0,0 +1,529 @@
+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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/RelatedModelsListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/RelatedModelsListenerTest.php
new file mode 100644
index 000000000..cbff0ffd2
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/RelatedModelsListenerTest.php
@@ -0,0 +1,826 @@
+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();
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ScaffoldListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ScaffoldListenerTest.php
new file mode 100644
index 000000000..6ef834438
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ScaffoldListenerTest.php
@@ -0,0 +1,323 @@
+ '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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/SearchListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/SearchListenerTest.php
new file mode 100644
index 000000000..bbf5cd07f
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/SearchListenerTest.php
@@ -0,0 +1,719 @@
+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');
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Error/CrudExceptionRendererTest.php b/web/api/app/Plugin/Crud/Test/Case/Error/CrudExceptionRendererTest.php
new file mode 100644
index 000000000..021fd1310
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Error/CrudExceptionRendererTest.php
@@ -0,0 +1,528 @@
+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']);
+ }
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Lib/Panel/CrudPanelTest.php b/web/api/app/Plugin/Crud/Test/Case/Lib/Panel/CrudPanelTest.php
new file mode 100644
index 000000000..c3aa1bbe0
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Lib/Panel/CrudPanelTest.php
@@ -0,0 +1,89 @@
+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');
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Routing/Filter/FakeHeadFilterTest.php b/web/api/app/Plugin/Crud/Test/Case/Routing/Filter/FakeHeadFilterTest.php
new file mode 100644
index 000000000..e5e328b23
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Routing/Filter/FakeHeadFilterTest.php
@@ -0,0 +1,51 @@
+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');
+ }
+}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Routing/Filter/HttpMethodFilterTest.php b/web/api/app/Plugin/Crud/Test/Case/Routing/Filter/HttpMethodFilterTest.php
new file mode 100644
index 000000000..72ed559b6
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Case/Routing/Filter/HttpMethodFilterTest.php
@@ -0,0 +1,207 @@
+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');
+ }
+}
diff --git a/web/api/app/Plugin/Crud/Test/Fixture/PostsTagFixture.php b/web/api/app/Plugin/Crud/Test/Fixture/PostsTagFixture.php
new file mode 100644
index 000000000..1c112725e
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Fixture/PostsTagFixture.php
@@ -0,0 +1,35 @@
+ 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(
+
+ );
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Support/CrudControllerTestCase.php b/web/api/app/Plugin/Crud/Test/Support/CrudControllerTestCase.php
new file mode 100644
index 000000000..d2f949fd1
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Support/CrudControllerTestCase.php
@@ -0,0 +1,169 @@
+ 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;
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/Test/Support/CrudTestCase.php b/web/api/app/Plugin/Crud/Test/Support/CrudTestCase.php
new file mode 100644
index 000000000..9dc02eee1
--- /dev/null
+++ b/web/api/app/Plugin/Crud/Test/Support/CrudTestCase.php
@@ -0,0 +1,168 @@
+ 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;
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/View/CrudJsonView.php b/web/api/app/Plugin/Crud/View/CrudJsonView.php
new file mode 100644
index 000000000..03a8ffdd2
--- /dev/null
+++ b/web/api/app/Plugin/Crud/View/CrudJsonView.php
@@ -0,0 +1,48 @@
+ $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);
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/View/CrudXmlView.php b/web/api/app/Plugin/Crud/View/CrudXmlView.php
new file mode 100644
index 000000000..75b6d934a
--- /dev/null
+++ b/web/api/app/Plugin/Crud/View/CrudXmlView.php
@@ -0,0 +1,52 @@
+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();
+ }
+
+}
diff --git a/web/api/app/Plugin/Crud/View/Elements/crud_panel.ctp b/web/api/app/Plugin/Crud/View/Elements/crud_panel.ctp
new file mode 100644
index 000000000..501f47a42
--- /dev/null
+++ b/web/api/app/Plugin/Crud/View/Elements/crud_panel.ctp
@@ -0,0 +1,17 @@
+= __d('crud', 'Config'); ?>
+ $crudDebugKitData['action'],
+ __d('crud', 'Component') => $crudDebugKitData['component'],
+ __d('crud', 'Listeners') => $crudDebugKitData['listeners']
+);
+
+echo $this->Toolbar->makeNeatArray($config);
+?>
+= __d('crud', 'Events triggered'); ?>
+Toolbar->makeNeatArray($crudDebugKitData['events']);
diff --git a/web/api/app/Plugin/Crud/composer.json b/web/api/app/Plugin/Crud/composer.json
new file mode 100644
index 000000000..679570e98
--- /dev/null
+++ b/web/api/app/Plugin/Crud/composer.json
@@ -0,0 +1,57 @@
+{
+ "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"
+ }
+}
diff --git a/web/api/app/View/Events/json/index.ctp b/web/api/app/View/Events/json/index.ctp
index 13cb1244b..d54386749 100644
--- a/web/api/app/View/Events/json/index.ctp
+++ b/web/api/app/View/Events/json/index.ctp
@@ -1 +1,5 @@
-echo json_encode($events);
+Paginator->params();
+ echo json_encode($array);
+?>
diff --git a/web/api/app/View/Logs/json/index.ctp b/web/api/app/View/Logs/json/index.ctp
new file mode 100644
index 000000000..32cbd51f8
--- /dev/null
+++ b/web/api/app/View/Logs/json/index.ctp
@@ -0,0 +1,5 @@
+Paginator->params();
+ echo json_encode($array);
+?>
diff --git a/web/api/app/View/View/Configs/json/edit.ctp b/web/api/app/View/View/Configs/json/edit.ctp
new file mode 100644
index 000000000..75fa758bd
--- /dev/null
+++ b/web/api/app/View/View/Configs/json/edit.ctp
@@ -0,0 +1 @@
+echo json_encode($config);
diff --git a/web/api/app/View/View/Configs/json/index.ctp b/web/api/app/View/View/Configs/json/index.ctp
new file mode 100644
index 000000000..86edf870a
--- /dev/null
+++ b/web/api/app/View/View/Configs/json/index.ctp
@@ -0,0 +1 @@
+echo json_encode($configs);
diff --git a/web/api/app/View/View/Configs/json/view.ctp b/web/api/app/View/View/Configs/json/view.ctp
new file mode 100644
index 000000000..75fa758bd
--- /dev/null
+++ b/web/api/app/View/View/Configs/json/view.ctp
@@ -0,0 +1 @@
+echo json_encode($config);
diff --git a/web/api/app/View/View/Configs/xml/index.ctp b/web/api/app/View/View/Configs/xml/index.ctp
new file mode 100644
index 000000000..b13a76093
--- /dev/null
+++ b/web/api/app/View/View/Configs/xml/index.ctp
@@ -0,0 +1,2 @@
+$xml = Xml::fromArray(array('response' => $configs));
+echo $xml->asXML();
diff --git a/web/api/app/View/View/Configs/xml/view.ctp b/web/api/app/View/View/Configs/xml/view.ctp
new file mode 100644
index 000000000..7987d32e7
--- /dev/null
+++ b/web/api/app/View/View/Configs/xml/view.ctp
@@ -0,0 +1,2 @@
+$xml = Xml::fromArray(array('response' => $config));
+echo $xml->asXML();
diff --git a/web/api/app/View/View/Elements/empty b/web/api/app/View/View/Elements/empty
new file mode 100644
index 000000000..e69de29bb
diff --git a/web/api/app/View/View/Emails/html/default.ctp b/web/api/app/View/View/Emails/html/default.ctp
new file mode 100644
index 000000000..e2bff19c0
--- /dev/null
+++ b/web/api/app/View/View/Emails/html/default.ctp
@@ -0,0 +1,25 @@
+
+ ' . $line . "
\n";
+endforeach;
+?>
\ No newline at end of file
diff --git a/web/api/app/View/View/Emails/text/default.ctp b/web/api/app/View/View/Emails/text/default.ctp
new file mode 100644
index 000000000..090b5c403
--- /dev/null
+++ b/web/api/app/View/View/Emails/text/default.ctp
@@ -0,0 +1,19 @@
+
+
\ No newline at end of file
diff --git a/web/api/app/View/View/Errors/error400.ctp b/web/api/app/View/View/Errors/error400.ctp
new file mode 100644
index 000000000..4c3850b28
--- /dev/null
+++ b/web/api/app/View/View/Errors/error400.ctp
@@ -0,0 +1,31 @@
+
+
+
+ :
+ '{$url}'"
+ ); ?>
+
+ 0):
+ echo $this->element('exception_stack_trace');
+endif;
+?>
diff --git a/web/api/app/View/View/Errors/error500.ctp b/web/api/app/View/View/Errors/error500.ctp
new file mode 100644
index 000000000..518b9ee77
--- /dev/null
+++ b/web/api/app/View/View/Errors/error500.ctp
@@ -0,0 +1,28 @@
+
+
+
+ :
+
+
+ 0):
+ echo $this->element('exception_stack_trace');
+endif;
+?>
diff --git a/web/api/app/View/View/Events/json/index.ctp b/web/api/app/View/View/Events/json/index.ctp
new file mode 100644
index 000000000..d54386749
--- /dev/null
+++ b/web/api/app/View/View/Events/json/index.ctp
@@ -0,0 +1,5 @@
+Paginator->params();
+ echo json_encode($array);
+?>
diff --git a/web/api/app/View/View/Events/json/view.ctp b/web/api/app/View/View/Events/json/view.ctp
new file mode 100644
index 000000000..b320feb4d
--- /dev/null
+++ b/web/api/app/View/View/Events/json/view.ctp
@@ -0,0 +1 @@
+echo json_encode($event);
diff --git a/web/api/app/View/View/Events/xml/index.ctp b/web/api/app/View/View/Events/xml/index.ctp
new file mode 100644
index 000000000..af960238f
--- /dev/null
+++ b/web/api/app/View/View/Events/xml/index.ctp
@@ -0,0 +1,2 @@
+$xml = Xml::fromArray(array('response' => $events));
+echo $xml->asXML();
diff --git a/web/api/app/View/View/Events/xml/view.ctp b/web/api/app/View/View/Events/xml/view.ctp
new file mode 100644
index 000000000..7f64e422f
--- /dev/null
+++ b/web/api/app/View/View/Events/xml/view.ctp
@@ -0,0 +1,2 @@
+$xml = Xml::fromArray(array('response' => $event));
+echo $xml->asXML();
diff --git a/web/api/app/View/View/Helper/AppHelper.php b/web/api/app/View/View/Helper/AppHelper.php
new file mode 100644
index 000000000..9097d33f0
--- /dev/null
+++ b/web/api/app/View/View/Helper/AppHelper.php
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+ fetch('content'); ?>
+
+ This email was sent using the CakePHP Framework
+
+
\ No newline at end of file
diff --git a/web/api/app/View/View/Layouts/Emails/text/default.ctp b/web/api/app/View/View/Layouts/Emails/text/default.ctp
new file mode 100644
index 000000000..ee624de45
--- /dev/null
+++ b/web/api/app/View/View/Layouts/Emails/text/default.ctp
@@ -0,0 +1,21 @@
+
+fetch('content'); ?>
+
+This email was sent using the CakePHP Framework, http://cakephp.org.
diff --git a/web/api/app/View/View/Layouts/ajax.ctp b/web/api/app/View/View/Layouts/ajax.ctp
new file mode 100644
index 000000000..0f9a4fb62
--- /dev/null
+++ b/web/api/app/View/View/Layouts/ajax.ctp
@@ -0,0 +1,19 @@
+
+fetch('content'); ?>
diff --git a/web/api/app/View/View/Layouts/default.ctp b/web/api/app/View/View/Layouts/default.ctp
new file mode 100644
index 000000000..38dececbc
--- /dev/null
+++ b/web/api/app/View/View/Layouts/default.ctp
@@ -0,0 +1,65 @@
+
+
+
+
+ Html->charset(); ?>
+
+ :
+
+
+ Html->meta('icon');
+
+ echo $this->Html->css('cake.generic');
+
+ echo $this->fetch('meta');
+ echo $this->fetch('css');
+ echo $this->fetch('script');
+ ?>
+
+
+
+
+
+
+ Session->flash(); ?>
+
+ fetch('content'); ?>
+
+
+
+ element('sql_dump'); ?>
+
+
diff --git a/web/api/app/View/View/Layouts/error.ctp b/web/api/app/View/View/Layouts/error.ctp
new file mode 100644
index 000000000..e9d738178
--- /dev/null
+++ b/web/api/app/View/View/Layouts/error.ctp
@@ -0,0 +1,61 @@
+
+
+
+
+ Html->charset(); ?>
+
+ :
+
+
+ Html->meta('icon');
+
+ echo $this->Html->css('cake.generic');
+
+ echo $this->fetch('meta');
+ echo $this->fetch('css');
+ echo $this->fetch('script');
+ ?>
+
+
+
+
+
+
+ Session->flash(); ?>
+
+ fetch('content'); ?>
+
+
+
+ element('sql_dump'); ?>
+
+
diff --git a/web/api/app/View/View/Layouts/flash.ctp b/web/api/app/View/View/Layouts/flash.ctp
new file mode 100644
index 000000000..cd79f5008
--- /dev/null
+++ b/web/api/app/View/View/Layouts/flash.ctp
@@ -0,0 +1,37 @@
+
+
+
+
+Html->charset(); ?>
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/api/app/View/View/Layouts/js/default.ctp b/web/api/app/View/View/Layouts/js/default.ctp
new file mode 100644
index 000000000..7239b5dae
--- /dev/null
+++ b/web/api/app/View/View/Layouts/js/default.ctp
@@ -0,0 +1,2 @@
+
+
diff --git a/web/api/app/View/View/Layouts/rss/default.ctp b/web/api/app/View/View/Layouts/rss/default.ctp
new file mode 100644
index 000000000..26d875eda
--- /dev/null
+++ b/web/api/app/View/View/Layouts/rss/default.ctp
@@ -0,0 +1,14 @@
+Rss->document(
+ $this->Rss->channel(
+ array(), $channel, $this->fetch('content')
+ )
+);
+?>
diff --git a/web/api/app/View/View/Layouts/xml/default.ctp b/web/api/app/View/View/Layouts/xml/default.ctp
new file mode 100644
index 000000000..fbd5ee0c3
--- /dev/null
+++ b/web/api/app/View/View/Layouts/xml/default.ctp
@@ -0,0 +1 @@
+fetch('content'); ?>
diff --git a/web/api/app/View/View/Logs/json/index.ctp b/web/api/app/View/View/Logs/json/index.ctp
new file mode 100644
index 000000000..32cbd51f8
--- /dev/null
+++ b/web/api/app/View/View/Logs/json/index.ctp
@@ -0,0 +1,5 @@
+Paginator->params();
+ echo json_encode($array);
+?>
diff --git a/web/api/app/View/View/Monitors/json/edit.ctp b/web/api/app/View/View/Monitors/json/edit.ctp
new file mode 100644
index 000000000..77d2dd08b
--- /dev/null
+++ b/web/api/app/View/View/Monitors/json/edit.ctp
@@ -0,0 +1,2 @@
+echo json_encode($message);
+echo json_encode($monitor);
diff --git a/web/api/app/View/View/Monitors/json/index.ctp b/web/api/app/View/View/Monitors/json/index.ctp
new file mode 100644
index 000000000..facf965d4
--- /dev/null
+++ b/web/api/app/View/View/Monitors/json/index.ctp
@@ -0,0 +1 @@
+echo json_encode($monitors);
diff --git a/web/api/app/View/View/Monitors/json/view.ctp b/web/api/app/View/View/Monitors/json/view.ctp
new file mode 100644
index 000000000..acfced9d0
--- /dev/null
+++ b/web/api/app/View/View/Monitors/json/view.ctp
@@ -0,0 +1 @@
+echo json_encode($monitor);
diff --git a/web/api/app/View/View/Monitors/xml/edit.ctp b/web/api/app/View/View/Monitors/xml/edit.ctp
new file mode 100644
index 000000000..09fb8979a
--- /dev/null
+++ b/web/api/app/View/View/Monitors/xml/edit.ctp
@@ -0,0 +1,2 @@
+$xml = Xml::fromArray(array('response' => $message));
+echo $xml->asXML();
diff --git a/web/api/app/View/View/Monitors/xml/index.ctp b/web/api/app/View/View/Monitors/xml/index.ctp
new file mode 100644
index 000000000..37afc918b
--- /dev/null
+++ b/web/api/app/View/View/Monitors/xml/index.ctp
@@ -0,0 +1,2 @@
+$xml = Xml::fromArray(array('response' => $monitors));
+echo $xml->asXML();
diff --git a/web/api/app/View/View/Monitors/xml/view.ctp b/web/api/app/View/View/Monitors/xml/view.ctp
new file mode 100644
index 000000000..b33c6e79a
--- /dev/null
+++ b/web/api/app/View/View/Monitors/xml/view.ctp
@@ -0,0 +1,2 @@
+$xml = Xml::fromArray(array('response' => $monitor));
+echo $xml->asXML();
diff --git a/web/api/app/View/View/Pages/home.ctp b/web/api/app/View/View/Pages/home.ctp
new file mode 100644
index 000000000..082cc99b0
--- /dev/null
+++ b/web/api/app/View/View/Pages/home.ctp
@@ -0,0 +1,233 @@
+
+
+
+
+
+ 0):
+ Debugger::checkSecurityKeys();
+endif;
+?>
+
+
+
+ 1) Help me configure it
+ 2) I don't / can't use URL rewriting
+
+
+
+=')):
+ echo '';
+ echo __d('cake_dev', 'Your version of PHP is 5.2.8 or higher.');
+ echo ' ';
+ else:
+ echo '';
+ echo __d('cake_dev', 'Your version of PHP is too low. You need PHP 5.2.8 or higher to use CakePHP.');
+ echo ' ';
+ endif;
+?>
+
+
+ ';
+ echo __d('cake_dev', 'Your tmp directory is writable.');
+ echo '';
+ else:
+ echo '';
+ echo __d('cake_dev', 'Your tmp directory is NOT writable.');
+ echo ' ';
+ endif;
+ ?>
+
+
+ ';
+ echo __d('cake_dev', 'The %s is being used for core caching. To change the config edit %s', ''. $settings['engine'] . 'Engine ', 'APP/Config/core.php');
+ echo '';
+ else:
+ echo '';
+ echo __d('cake_dev', 'Your cache is NOT working. Please check the settings in %s', 'APP/Config/core.php');
+ echo ' ';
+ endif;
+ ?>
+
+
+ ';
+ echo __d('cake_dev', 'Your database configuration file is present.');
+ $filePresent = true;
+ echo '';
+ else:
+ echo '';
+ echo __d('cake_dev', 'Your database configuration file is NOT present.');
+ echo ' ';
+ echo __d('cake_dev', 'Rename %s to %s', 'APP/Config/database.php.default', 'APP/Config/database.php');
+ echo ' ';
+ endif;
+ ?>
+
+getMessage();
+ if (method_exists($connectionError, 'getAttributes')):
+ $attributes = $connectionError->getAttributes();
+ if (isset($errorMsg['message'])):
+ $errorMsg .= ' ' . $attributes['message'];
+ endif;
+ endif;
+ }
+?>
+
+ isConnected()):
+ echo '';
+ echo __d('cake_dev', 'CakePHP is able to connect to the database.');
+ echo ' ';
+ else:
+ echo '';
+ echo __d('cake_dev', 'CakePHP is NOT able to connect to the database.');
+ echo ' ';
+ echo $errorMsg;
+ echo ' ';
+ endif;
+ ?>
+
+
+';
+ echo __d('cake_dev', 'PCRE has not been compiled with Unicode support.');
+ echo ' ';
+ echo __d('cake_dev', 'Recompile PCRE with Unicode support by adding --enable-unicode-properties when configuring');
+ echo ' ';
+ endif;
+?>
+
+
+ ';
+ echo __d('cake_dev', 'DebugKit plugin is present');
+ echo '';
+ else:
+ echo '';
+ echo __d('cake_dev', 'DebugKit is not installed. It will help you inspect and debug different aspects of your application.');
+ echo ' ';
+ echo __d('cake_dev', 'You can install it from %s', $this->Html->link('GitHub', 'https://github.com/cakephp/debug_kit'));
+ echo ' ';
+ endif;
+ ?>
+
+
+
+
+
+To change its layout, edit: %s.
+You can also add some CSS styles for your pages at: %s.',
+ 'APP/View/Pages/home.ctp', 'APP/View/Layouts/default.ctp', 'APP/webroot/css');
+?>
+
+
+
+
+ Html->link(
+ sprintf('%s %s', __d('cake_dev', 'New'), __d('cake_dev', 'CakePHP 2.0 Docs')),
+ 'http://book.cakephp.org/2.0/en/',
+ array('target' => '_blank', 'escape' => false)
+ );
+ ?>
+
+
+ Html->link(
+ __d('cake_dev', 'The 15 min Blog Tutorial'),
+ 'http://book.cakephp.org/2.0/en/tutorials-and-examples/blog/blog.html',
+ array('target' => '_blank', 'escape' => false)
+ );
+ ?>
+
+
+
+
+
+
+ Html->link('DebugKit', 'https://github.com/cakephp/debug_kit') ?>:
+
+
+
+ Html->link('Localized', 'https://github.com/cakephp/localized') ?>:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/api/app/View/View/Scaffolds/empty b/web/api/app/View/View/Scaffolds/empty
new file mode 100644
index 000000000..e69de29bb
diff --git a/web/api/app/vendor/autoload.php b/web/api/app/vendor/autoload.php
new file mode 100644
index 000000000..0a2797817
--- /dev/null
+++ b/web/api/app/vendor/autoload.php
@@ -0,0 +1,7 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0 class loader
+ *
+ * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier
+ * @author Jordi Boggiano
+ */
+class ClassLoader
+{
+ // PSR-4
+ private $prefixLengthsPsr4 = array();
+ private $prefixDirsPsr4 = array();
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ private $prefixesPsr0 = array();
+ private $fallbackDirsPsr0 = array();
+
+ private $useIncludePath = false;
+ private $classMap = array();
+
+ private $classMapAuthoritative = false;
+
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
+ }
+
+ return array();
+ }
+
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param array $classMap Class to filename map
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ (array) $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-0 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ (array) $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 base directories
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return bool|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ includeFile($file);
+
+ return true;
+ }
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
+ if ('\\' == $class[0]) {
+ $class = substr($class, 1);
+ }
+
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative) {
+ return false;
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if ($file === null && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if ($file === null) {
+ // Remember that this class does not exist.
+ return $this->classMap[$class] = false;
+ }
+
+ return $file;
+ }
+
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
+ if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+ }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+ include $file;
+}
diff --git a/web/api/app/vendor/composer/autoload_classmap.php b/web/api/app/vendor/composer/autoload_classmap.php
new file mode 100644
index 000000000..7a91153b0
--- /dev/null
+++ b/web/api/app/vendor/composer/autoload_classmap.php
@@ -0,0 +1,9 @@
+ array($vendorDir . '/composer/installers/src'),
+);
diff --git a/web/api/app/vendor/composer/autoload_psr4.php b/web/api/app/vendor/composer/autoload_psr4.php
new file mode 100644
index 000000000..b265c64a2
--- /dev/null
+++ b/web/api/app/vendor/composer/autoload_psr4.php
@@ -0,0 +1,9 @@
+ $path) {
+ $loader->set($namespace, $path);
+ }
+
+ $map = require __DIR__ . '/autoload_psr4.php';
+ foreach ($map as $namespace => $path) {
+ $loader->setPsr4($namespace, $path);
+ }
+
+ $classMap = require __DIR__ . '/autoload_classmap.php';
+ if ($classMap) {
+ $loader->addClassMap($classMap);
+ }
+
+ $loader->register(true);
+
+ return $loader;
+ }
+}
+
+function composerRequiredfd8518a66bb8898e7b22470609a6c8f($file)
+{
+ require $file;
+}
diff --git a/web/api/app/vendor/composer/installed.json b/web/api/app/vendor/composer/installed.json
new file mode 100644
index 000000000..39d771174
--- /dev/null
+++ b/web/api/app/vendor/composer/installed.json
@@ -0,0 +1,167 @@
+[
+ {
+ "name": "composer/installers",
+ "version": "v1.0.21",
+ "version_normalized": "1.0.21.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/installers.git",
+ "reference": "d64e23fce42a4063d63262b19b8e7c0f3b5e4c45"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/installers/zipball/d64e23fce42a4063d63262b19b8e7c0f3b5e4c45",
+ "reference": "d64e23fce42a4063d63262b19b8e7c0f3b5e4c45",
+ "shasum": ""
+ },
+ "replace": {
+ "roundcube/plugin-installer": "*",
+ "shama/baton": "*"
+ },
+ "require-dev": {
+ "composer/composer": "1.0.*@dev",
+ "phpunit/phpunit": "4.1.*"
+ },
+ "time": "2015-02-18 17:17:01",
+ "type": "composer-installer",
+ "extra": {
+ "class": "Composer\\Installers\\Installer",
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-0": {
+ "Composer\\Installers\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Kyle Robinson Young",
+ "email": "kyle@dontkry.com",
+ "homepage": "https://github.com/shama"
+ }
+ ],
+ "description": "A multi-framework Composer library installer",
+ "homepage": "http://composer.github.com/installers/",
+ "keywords": [
+ "Craft",
+ "Dolibarr",
+ "Hurad",
+ "MODX Evo",
+ "OXID",
+ "SMF",
+ "Thelia",
+ "WolfCMS",
+ "agl",
+ "aimeos",
+ "annotatecms",
+ "bitrix",
+ "cakephp",
+ "chef",
+ "codeigniter",
+ "concrete5",
+ "croogo",
+ "dokuwiki",
+ "drupal",
+ "elgg",
+ "fuelphp",
+ "grav",
+ "installer",
+ "joomla",
+ "kohana",
+ "laravel",
+ "lithium",
+ "magento",
+ "mako",
+ "mediawiki",
+ "modulework",
+ "moodle",
+ "phpbb",
+ "piwik",
+ "ppi",
+ "puppet",
+ "roundcube",
+ "shopware",
+ "silverstripe",
+ "symfony",
+ "typo3",
+ "wordpress",
+ "zend",
+ "zikula"
+ ]
+ },
+ {
+ "name": "friendsofcake/crud",
+ "version": "3.0.10",
+ "version_normalized": "3.0.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/FriendsOfCake/crud.git",
+ "reference": "c3976f1478c681b0bbc132ec3a3e82c3984eeed5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/FriendsOfCake/crud/zipball/c3976f1478c681b0bbc132ec3a3e82c3984eeed5",
+ "reference": "c3976f1478c681b0bbc132ec3a3e82c3984eeed5",
+ "shasum": ""
+ },
+ "require": {
+ "composer/installers": "*"
+ },
+ "suggest": {
+ "cakedc/search": "If you want to use the Search Listener"
+ },
+ "time": "2015-04-18 19:08:17",
+ "type": "cakephp-plugin",
+ "extra": {
+ "installer-name": "Crud"
+ },
+ "installation-source": "dist",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Winther",
+ "homepage": "http://cakephp.nu/",
+ "role": "Author"
+ },
+ {
+ "name": "José Lorenzo RodrÃguez",
+ "homepage": "https://github.com/lorenzo",
+ "role": "Contributor"
+ },
+ {
+ "name": "Andy Dawson",
+ "homepage": "https://github.com/ad7six",
+ "role": "Contributor"
+ },
+ {
+ "name": "ADmad",
+ "homepage": "https://github.com/admad",
+ "role": "Contributor"
+ }
+ ],
+ "description": "CakePHP Application development on steroids - rapid prototyping / scaffolding & production ready code - XML / JSON APIs and more",
+ "homepage": "https://github.com/FriendsOfCake/crud",
+ "keywords": [
+ "bake",
+ "cake",
+ "cakephp",
+ "create",
+ "crud",
+ "delete",
+ "retrieve",
+ "scaffold",
+ "scaffolding",
+ "update"
+ ]
+ }
+]
diff --git a/web/api/app/vendor/composer/installers/.editorconfig b/web/api/app/vendor/composer/installers/.editorconfig
new file mode 100644
index 000000000..153cf3ef5
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/.editorconfig
@@ -0,0 +1,10 @@
+; top-most EditorConfig file
+root = true
+
+; Unix-style newlines
+[*]
+end_of_line = LF
+
+[*.php]
+indent_style = space
+indent_size = 4
diff --git a/web/api/app/vendor/composer/installers/.gitignore b/web/api/app/vendor/composer/installers/.gitignore
new file mode 100644
index 000000000..ff7f293dc
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/.gitignore
@@ -0,0 +1,3 @@
+vendor/
+composer.lock
+.idea/
diff --git a/web/api/app/vendor/composer/installers/.travis.yml b/web/api/app/vendor/composer/installers/.travis.yml
new file mode 100644
index 000000000..81ca8e101
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/.travis.yml
@@ -0,0 +1,14 @@
+language: php
+
+php:
+ - 5.3
+ - 5.4
+ - 5.5
+ - 5.6
+ - hhvm
+
+before_script:
+ - curl -s http://getcomposer.org/installer | php -- --quiet
+ - php composer.phar install --dev
+
+script: phpunit
diff --git a/web/api/app/vendor/composer/installers/LICENSE b/web/api/app/vendor/composer/installers/LICENSE
new file mode 100644
index 000000000..85f97fc79
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2012 Kyle Robinson Young
+
+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.
\ No newline at end of file
diff --git a/web/api/app/vendor/composer/installers/README.md b/web/api/app/vendor/composer/installers/README.md
new file mode 100644
index 000000000..b33177198
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/README.md
@@ -0,0 +1,191 @@
+# A Multi-Framework [Composer](http://getcomposer.org) Library Installer
+
+[](http://travis-ci.org/composer/installers)
+
+This is for PHP package authors to require in their `composer.json`. It will
+install their package to the correct location based on the specified package
+type.
+
+The goal of `installers` is to be a simple package type to install path map.
+Users can also customize the install path per package and package authors can
+modify the package name upon installing.
+
+`installers` isn't intended on replacing all custom installers. If your
+package requires special installation handling then by all means, create a
+custom installer to handle it.
+
+**Natively Supported Frameworks**:
+
+The following frameworks natively work with Composer and will be
+installed to the default `vendor` directory. `composer/installers`
+is not needed to install packages with these frameworks:
+
+* Aura
+* Symfony2
+* Yii
+* Yii2
+
+**Current Supported Package Types**:
+
+> Stable types are marked as **bold**, this means that installation paths
+> for those type will not be changed. Any adjustment for those types would
+> require creation of brand new type that will cover required changes.
+
+| Framework | Types
+| --------- | -----
+| Aimeos | `aimeos-extension`
+| Asgard | `asgard-module` `asgard-theme`
+| AGL | `agl-module`
+| AnnotateCms | `annotatecms-module` `annotatecms-component` `annotatecms-service`
+| Bitrix | `bitrix-module` `bitrix-component` `bitrix-theme`
+| CakePHP 2+ | **`cakephp-plugin`**
+| Chef | `chef-cookbook` `chef-role`
+| CCFramework | `ccframework-ship` `ccframework-theme`
+| CodeIgniter | `codeigniter-library` `codeigniter-third-party` `codeigniter-module`
+| concrete5 | `concrete5-block` `concrete5-package` `concrete5-theme` `concrete5-update`
+| Craft | `craft-plugin`
+| Croogo | `croogo-plugin` `croogo-theme`
+| DokuWiki | `dokuwiki-plugin` `dokuwiki-template`
+| Dolibarr | `dolibarr-module`
+| Drupal | `drupal-module` `drupal-theme` `drupal-library` `drupal-profile` `drupal-drush`
+| Elgg | `elgg-plugin`
+| FuelPHP v1.x | `fuel-module` `fuel-package` `fuel-theme`
+| FuelPHP v2.x | `fuelphp-component`
+| Grav | `grav-plugin` `grav-theme`
+| Hurad | `hurad-plugin` `hurad-theme`
+| Joomla | `joomla-component` `joomla-module` `joomla-template` `joomla-plugin` `joomla-library`
+| Kirby | **`kirby-plugin`**
+| Kohana | **`kohana-module`**
+| Laravel | `laravel-library`
+| Lithium | **`lithium-library` `lithium-source`**
+| Magento | `magento-library` `magento-skin` `magento-theme`
+| Mako | `mako-package`
+| MODX Evo | `modxevo-snippet` `modxevo-plugin` `modxevo-module` `modxevo-template` `modxevo-lib`
+| MediaWiki | `mediawiki-extension`
+| October | **`october-module` `october-plugin` `october-theme`**
+| OXID | `oxid-module` `oxid-theme` `oxid-out`
+| MODULEWork | `modulework-module`
+| Moodle | `moodle-*` (Please [check source](https://raw.githubusercontent.com/composer/installers/master/src/Composer/Installers/MoodleInstaller.php) for all supported types)
+| Piwik | `piwik-plugin`
+| phpBB | `phpbb-extension` `phpbb-style` `phpbb-language`
+| Pimcore | `pimcore-plugin`
+| PPI | **`ppi-module`**
+| Puppet | `puppet-module`
+| REDAXO | `redaxo-addon`
+| Roundcube | `roundcube-plugin`
+| shopware | `shopware-backend-plugin` `shopware-core-plugin` `shopware-frontend-plugin` `shopware-theme`
+| SilverStripe | `silverstripe-module` `silverstripe-theme`
+| SMF | `smf-module` `smf-theme`
+| symfony1 | **`symfony1-plugin`**
+| Tusk | `tusk-task` `tusk-command` `tusk-asset`
+| TYPO3 Flow | `typo3-flow-package` `typo3-flow-framework` `typo3-flow-plugin` `typo3-flow-site` `typo3-flow-boilerplate` `typo3-flow-build`
+| TYPO3 CMS | `typo3-cms-extension`
+| Wolf CMS | `wolfcms-plugin`
+| WordPress | `wordpress-plugin` `wordpress-theme` `wordpress-muplugin`
+| Zend | `zend-library` `zend-extra` `zend-module`
+| Zikula | `zikula-module` `zikula-theme`
+| Prestashop | `prestashop-module` `prestashop-theme`
+
+## Example `composer.json` File
+
+This is an example for a CakePHP plugin. The only important parts to set in your
+composer.json file are `"type": "cakephp-plugin"` which describes what your
+package is and `"require": { "composer/installers": "~1.0" }` which tells composer
+to load the custom installers.
+
+```json
+{
+ "name": "you/ftp",
+ "type": "cakephp-plugin",
+ "require": {
+ "composer/installers": "~1.0"
+ }
+}
+```
+
+This would install your package to the `Plugin/Ftp/` folder of a CakePHP app
+when a user runs `php composer.phar install`.
+
+So submit your packages to [packagist.org](http://packagist.org)!
+
+## Custom Install Paths
+
+If you are consuming a package that uses the `composer/installers` you can
+override the install path with the following extra in your `composer.json`:
+
+```json
+{
+ "extra": {
+ "installer-paths": {
+ "your/custom/path/{$name}/": ["shama/ftp", "vendor/package"]
+ }
+ }
+}
+```
+
+A package type can have a custom installation path with a `type:` prefix.
+
+``` json
+{
+ "extra": {
+ "installer-paths": {
+ "your/custom/path/{$name}/": ["type:wordpress-plugin"]
+ }
+ }
+}
+```
+
+This would use your custom path for each of the listed packages. The available
+variables to use in your paths are: `{$name}`, `{$vendor}`, `{$type}`.
+
+## Custom Install Names
+
+If you're a package author and need your package to be named differently when
+installed consider using the `installer-name` extra.
+
+For example you have a package named `shama/cakephp-ftp` with the type
+`cakephp-plugin`. Installing with `composer/installers` would install to the
+path `Plugin/CakephpFtp`. Due to the strict naming conventions, you as a
+package author actually need the package to be named and installed to
+`Plugin/Ftp`. Using the following config within your **package** `composer.json`
+will allow this:
+
+```json
+{
+ "name": "shama/cakephp-ftp",
+ "type": "cakephp-plugin",
+ "extra": {
+ "installer-name": "Ftp"
+ }
+}
+```
+
+Please note the name entered into `installer-name` will be the final and will
+not be inflected.
+
+## Contribute!
+
+* [Fork and clone](https://help.github.com/articles/fork-a-repo).
+* Run the command `php composer.phar install --dev` to install the dev
+ dependencies. See [Composer](https://github.com/composer/composer#installation--usage).
+* Use the command `phpunit` to run the tests. See [PHPUnit](http://phpunit.de).
+* Create a branch, commit, push and send us a
+ [pull request](https://help.github.com/articles/using-pull-requests).
+
+To ensure a consistent code base, you should make sure the code follows the
+[Coding Standards](http://symfony.com/doc/2.0/contributing/code/standards.html)
+which we borrowed from Symfony.
+
+If you would like to help, please take a look at the list of
+[issues](https://github.com/composer/installers/issues).
+
+### Should we allow dynamic package types or paths? No.
+What are they? The ability for a package author to determine where a package
+will be installed either through setting the path directly in their
+`composer.json` or through a dynamic package type: `"type":
+"framework-install-here"`.
+
+It has been proposed many times. Even implemented once early on and then
+removed. `installers` won't do this because it would allow a single package
+author to wipe out entire folders without the user's consent. That user would
+then come here to yell at us.
diff --git a/web/api/app/vendor/composer/installers/composer.json b/web/api/app/vendor/composer/installers/composer.json
new file mode 100644
index 000000000..6ee931ee9
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/composer.json
@@ -0,0 +1,77 @@
+{
+ "name": "composer/installers",
+ "type": "composer-installer",
+ "license": "MIT",
+ "description": "A multi-framework Composer library installer",
+ "keywords": [
+ "installer",
+ "Aimeos",
+ "AGL",
+ "AnnotateCms",
+ "Bitrix",
+ "CakePHP",
+ "Chef",
+ "CodeIgniter",
+ "concrete5",
+ "Craft",
+ "Croogo",
+ "DokuWiki",
+ "Dolibarr",
+ "Drupal",
+ "Elgg",
+ "FuelPHP",
+ "Grav",
+ "Hurad",
+ "Joomla",
+ "Kohana",
+ "Laravel",
+ "Lithium",
+ "Magento",
+ "Mako",
+ "MODX Evo",
+ "MediaWiki",
+ "OXID",
+ "MODULEWork",
+ "Moodle",
+ "Piwik",
+ "phpBB",
+ "PPI",
+ "Puppet",
+ "Roundcube",
+ "shopware",
+ "SilverStripe",
+ "SMF",
+ "symfony",
+ "Thelia",
+ "TYPO3",
+ "WolfCMS",
+ "WordPress",
+ "Zend",
+ "Zikula"
+ ],
+ "homepage": "http://composer.github.com/installers/",
+ "authors": [
+ {
+ "name": "Kyle Robinson Young",
+ "email": "kyle@dontkry.com",
+ "homepage": "https://github.com/shama"
+ }
+ ],
+ "autoload": {
+ "psr-0": { "Composer\\Installers\\": "src/" }
+ },
+ "extra": {
+ "class": "Composer\\Installers\\Installer",
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "replace": {
+ "shama/baton": "*",
+ "roundcube/plugin-installer": "*"
+ },
+ "require-dev": {
+ "composer/composer": "1.0.*@dev",
+ "phpunit/phpunit": "4.1.*"
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/phpunit.xml.dist b/web/api/app/vendor/composer/installers/phpunit.xml.dist
new file mode 100644
index 000000000..cc5cc9915
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/phpunit.xml.dist
@@ -0,0 +1,25 @@
+
+
+
+
+
+ tests/Composer/Installers
+
+
+
+
+
+ src/Composer/Installers
+
+
+
\ No newline at end of file
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/AglInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/AglInstaller.php
new file mode 100644
index 000000000..01b8a4165
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/AglInstaller.php
@@ -0,0 +1,21 @@
+ 'More/{$name}/',
+ );
+
+ /**
+ * Format package name to CamelCase
+ */
+ public function inflectPackageVars($vars)
+ {
+ $vars['name'] = preg_replace_callback('/(?:^|_|-)(.?)/', function ($matches) {
+ return strtoupper($matches[1]);
+ }, $vars['name']);
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/AimeosInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/AimeosInstaller.php
new file mode 100644
index 000000000..79a0e958f
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/AimeosInstaller.php
@@ -0,0 +1,9 @@
+ 'ext/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php
new file mode 100644
index 000000000..89d7ad905
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php
@@ -0,0 +1,11 @@
+ 'addons/modules/{$name}/',
+ 'component' => 'addons/components/{$name}/',
+ 'service' => 'addons/services/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/AsgardInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/AsgardInstaller.php
new file mode 100644
index 000000000..995ee2b49
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/AsgardInstaller.php
@@ -0,0 +1,45 @@
+ 'Modules/{$name}/',
+ 'theme' => 'Themes/{$name}/'
+ );
+
+ /**
+ * Format package name.
+ *
+ * For package type asgard-module, cut off a trailing '-plugin' if present.
+ *
+ * For package type asgard-theme, cut off a trailing '-theme' if present.
+ *
+ */
+ public function inflectPackageVars($vars)
+ {
+ if ($vars['type'] === 'asgard-module') {
+ return $this->inflectPluginVars($vars);
+ }
+
+ if ($vars['type'] === 'asgard-theme') {
+ return $this->inflectThemeVars($vars);
+ }
+
+ return $vars;
+ }
+
+ protected function inflectPluginVars($vars)
+ {
+ $vars['name'] = ucfirst(preg_replace('/-module/', '', $vars['name']));
+
+ return $vars;
+ }
+
+ protected function inflectThemeVars($vars)
+ {
+ $vars['name'] = ucfirst(preg_replace('/-theme$/', '', $vars['name']));
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/BaseInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/BaseInstaller.php
new file mode 100644
index 000000000..cc27d3e28
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/BaseInstaller.php
@@ -0,0 +1,131 @@
+composer = $composer;
+ $this->package = $package;
+ }
+
+ /**
+ * Return the install path based on package type.
+ *
+ * @param PackageInterface $package
+ * @param string $frameworkType
+ * @return string
+ */
+ public function getInstallPath(PackageInterface $package, $frameworkType = '')
+ {
+ $type = $this->package->getType();
+
+ $prettyName = $this->package->getPrettyName();
+ if (strpos($prettyName, '/') !== false) {
+ list($vendor, $name) = explode('/', $prettyName);
+ } else {
+ $vendor = '';
+ $name = $prettyName;
+ }
+
+ $availableVars = $this->inflectPackageVars(compact('name', 'vendor', 'type'));
+
+ $extra = $package->getExtra();
+ if (!empty($extra['installer-name'])) {
+ $availableVars['name'] = $extra['installer-name'];
+ }
+
+ if ($this->composer->getPackage()) {
+ $extra = $this->composer->getPackage()->getExtra();
+ if (!empty($extra['installer-paths'])) {
+ $customPath = $this->mapCustomInstallPaths($extra['installer-paths'], $prettyName, $type);
+ if ($customPath !== false) {
+ return $this->templatePath($customPath, $availableVars);
+ }
+ }
+ }
+
+ $packageType = substr($type, strlen($frameworkType) + 1);
+ $locations = $this->getLocations();
+ if (!isset($locations[$packageType])) {
+ throw new \InvalidArgumentException(sprintf('Package type "%s" is not supported', $type));
+ }
+
+ return $this->templatePath($locations[$packageType], $availableVars);
+ }
+
+ /**
+ * For an installer to override to modify the vars per installer.
+ *
+ * @param array $vars
+ * @return array
+ */
+ public function inflectPackageVars($vars)
+ {
+ return $vars;
+ }
+
+ /**
+ * Gets the installer's locations
+ *
+ * @return array
+ */
+ public function getLocations()
+ {
+ return $this->locations;
+ }
+
+ /**
+ * Replace vars in a path
+ *
+ * @param string $path
+ * @param array $vars
+ * @return string
+ */
+ protected function templatePath($path, array $vars = array())
+ {
+ if (strpos($path, '{') !== false) {
+ extract($vars);
+ preg_match_all('@\{\$([A-Za-z0-9_]*)\}@i', $path, $matches);
+ if (!empty($matches[1])) {
+ foreach ($matches[1] as $var) {
+ $path = str_replace('{$' . $var . '}', $$var, $path);
+ }
+ }
+ }
+
+ return $path;
+ }
+
+ /**
+ * Search through a passed paths array for a custom install path.
+ *
+ * @param array $paths
+ * @param string $name
+ * @param string $type
+ * @return string
+ */
+ protected function mapCustomInstallPaths(array $paths, $name, $type)
+ {
+ foreach ($paths as $path => $names) {
+ if (in_array($name, $names) || in_array('type:' . $type, $names)) {
+ return $path;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php
new file mode 100644
index 000000000..48a8367ab
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php
@@ -0,0 +1,11 @@
+ 'local/modules/{$name}/',
+ 'component' => 'local/components/{$name}/',
+ 'theme' => 'local/templates/{$name}/'
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/CakePHPInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/CakePHPInstaller.php
new file mode 100644
index 000000000..cbeb60b80
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/CakePHPInstaller.php
@@ -0,0 +1,78 @@
+ 'Plugin/{$name}/',
+ );
+
+ /**
+ * Format package name to CamelCase
+ */
+ public function inflectPackageVars($vars)
+ {
+ if ($this->matchesCakeVersion('>=', '3.0.0')) {
+ return $vars;
+ }
+
+ $nameParts = explode('/', $vars['name']);
+ foreach ($nameParts as &$value) {
+ $value = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $value));
+ $value = str_replace(array('-', '_'), ' ', $value);
+ $value = str_replace(' ', '', ucwords($value));
+ }
+ $vars['name'] = implode('/', $nameParts);
+
+ return $vars;
+ }
+
+ /**
+ * Change the default plugin location when cakephp >= 3.0
+ */
+ public function getLocations()
+ {
+ if ($this->matchesCakeVersion('>=', '3.0.0')) {
+ $this->locations['plugin'] = $this->composer->getConfig()->get('vendor-dir') . '/{$vendor}/{$name}/';
+ }
+ return $this->locations;
+ }
+
+ /**
+ * Check if CakePHP version matches against a version
+ *
+ * @param string $matcher
+ * @param string $version
+ * @return bool
+ */
+ protected function matchesCakeVersion($matcher, $version)
+ {
+ $repositoryManager = $this->composer->getRepositoryManager();
+ if ($repositoryManager) {
+ $repos = $repositoryManager->getLocalRepository();
+ if (!$repos) {
+ return false;
+ }
+ $cake3 = new MultiConstraint(array(
+ new VersionConstraint($matcher, $version),
+ new VersionConstraint('!=', '9999999-dev'),
+ ));
+ $pool = new Pool('dev');
+ $pool->addRepository($repos);
+ $packages = $pool->whatProvides('cakephp/cakephp');
+ foreach ($packages as $package) {
+ $installed = new VersionConstraint('=', $package->getVersion());
+ if ($cake3->matches($installed)) {
+ return true;
+ break;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ChefInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ChefInstaller.php
new file mode 100644
index 000000000..ab2f9aad8
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ChefInstaller.php
@@ -0,0 +1,11 @@
+ 'Chef/{$vendor}/{$name}/',
+ 'role' => 'Chef/roles/{$name}/',
+ );
+}
+
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php
new file mode 100644
index 000000000..c887815c9
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php
@@ -0,0 +1,10 @@
+ 'CCF/orbit/{$name}/',
+ 'theme' => 'CCF/app/themes/{$name}/',
+ );
+}
\ No newline at end of file
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php
new file mode 100644
index 000000000..3b4a4ece1
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php
@@ -0,0 +1,11 @@
+ 'application/libraries/{$name}/',
+ 'third-party' => 'application/third_party/{$name}/',
+ 'module' => 'application/modules/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/Concrete5Installer.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/Concrete5Installer.php
new file mode 100644
index 000000000..4d398a445
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/Concrete5Installer.php
@@ -0,0 +1,12 @@
+ 'blocks/{$name}/',
+ 'package' => 'packages/{$name}/',
+ 'theme' => 'themes/{$name}/',
+ 'update' => 'updates/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/CraftInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/CraftInstaller.php
new file mode 100644
index 000000000..dc3be8d1a
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/CraftInstaller.php
@@ -0,0 +1,9 @@
+ 'craft/plugins/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/CroogoInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/CroogoInstaller.php
new file mode 100644
index 000000000..d94219d3a
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/CroogoInstaller.php
@@ -0,0 +1,21 @@
+ 'Plugin/{$name}/',
+ 'theme' => 'View/Themed/{$name}/',
+ );
+
+ /**
+ * Format package name to CamelCase
+ */
+ public function inflectPackageVars($vars)
+ {
+ $vars['name'] = strtolower(str_replace(array('-', '_'), ' ', $vars['name']));
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/DokuWikiInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/DokuWikiInstaller.php
new file mode 100644
index 000000000..cfd638d5f
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/DokuWikiInstaller.php
@@ -0,0 +1,50 @@
+ 'lib/plugins/{$name}/',
+ 'template' => 'lib/tpl/{$name}/',
+ );
+
+ /**
+ * Format package name.
+ *
+ * For package type dokuwiki-plugin, cut off a trailing '-plugin',
+ * or leading dokuwiki_ if present.
+ *
+ * For package type dokuwiki-template, cut off a trailing '-template' if present.
+ *
+ */
+ public function inflectPackageVars($vars)
+ {
+
+ if ($vars['type'] === 'dokuwiki-plugin') {
+ return $this->inflectPluginVars($vars);
+ }
+
+ if ($vars['type'] === 'dokuwiki-template') {
+ return $this->inflectTemplateVars($vars);
+ }
+
+ return $vars;
+ }
+
+ protected function inflectPluginVars($vars)
+ {
+ $vars['name'] = preg_replace('/-plugin$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/^dokuwiki_?-?/', '', $vars['name']);
+
+ return $vars;
+ }
+
+ protected function inflectTemplateVars($vars)
+ {
+ $vars['name'] = preg_replace('/-template$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/^dokuwiki_?-?/', '', $vars['name']);
+
+ return $vars;
+ }
+
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/DolibarrInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/DolibarrInstaller.php
new file mode 100644
index 000000000..21f7e8e80
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/DolibarrInstaller.php
@@ -0,0 +1,16 @@
+
+ */
+class DolibarrInstaller extends BaseInstaller
+{
+ //TODO: Add support for scripts and themes
+ protected $locations = array(
+ 'module' => 'htdocs/custom/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php
new file mode 100644
index 000000000..179413145
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php
@@ -0,0 +1,14 @@
+ 'core/',
+ 'module' => 'modules/{$name}/',
+ 'theme' => 'themes/{$name}/',
+ 'library' => 'libraries/{$name}/',
+ 'profile' => 'profiles/{$name}/',
+ 'drush' => 'drush/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ElggInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ElggInstaller.php
new file mode 100644
index 000000000..c0bb609f4
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ElggInstaller.php
@@ -0,0 +1,9 @@
+ 'mod/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelInstaller.php
new file mode 100644
index 000000000..6eba2e34f
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelInstaller.php
@@ -0,0 +1,11 @@
+ 'fuel/app/modules/{$name}/',
+ 'package' => 'fuel/packages/{$name}/',
+ 'theme' => 'fuel/app/themes/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelphpInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelphpInstaller.php
new file mode 100644
index 000000000..29d980b30
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelphpInstaller.php
@@ -0,0 +1,9 @@
+ 'components/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/GravInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/GravInstaller.php
new file mode 100644
index 000000000..dbe63e07e
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/GravInstaller.php
@@ -0,0 +1,30 @@
+ 'user/plugins/{$name}/',
+ 'theme' => 'user/themes/{$name}/',
+ );
+
+ /**
+ * Format package name
+ *
+ * @param array $vars
+ *
+ * @return array
+ */
+ public function inflectPackageVars($vars)
+ {
+ $restrictedWords = implode('|', array_keys($this->locations));
+
+ $vars['name'] = strtolower($vars['name']);
+ $vars['name'] = preg_replace('/^(?:grav-)?(?:(?:'.$restrictedWords.')-)?(.*?)(?:-(?:'.$restrictedWords.'))?$/ui',
+ '$1',
+ $vars['name']
+ );
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/HuradInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/HuradInstaller.php
new file mode 100644
index 000000000..8fe017f0f
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/HuradInstaller.php
@@ -0,0 +1,25 @@
+ 'plugins/{$name}/',
+ 'theme' => 'plugins/{$name}/',
+ );
+
+ /**
+ * Format package name to CamelCase
+ */
+ public function inflectPackageVars($vars)
+ {
+ $nameParts = explode('/', $vars['name']);
+ foreach ($nameParts as &$value) {
+ $value = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $value));
+ $value = str_replace(array('-', '_'), ' ', $value);
+ $value = str_replace(' ', '', ucwords($value));
+ }
+ $vars['name'] = implode('/', $nameParts);
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/Installer.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/Installer.php
new file mode 100644
index 000000000..63ba64ce3
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/Installer.php
@@ -0,0 +1,163 @@
+ 'AimeosInstaller',
+ 'asgard' => 'AsgardInstaller',
+ 'agl' => 'AglInstaller',
+ 'annotatecms' => 'AnnotateCmsInstaller',
+ 'bitrix' => 'BitrixInstaller',
+ 'cakephp' => 'CakePHPInstaller',
+ 'chef' => 'ChefInstaller',
+ 'ccframework' => 'ClanCatsFrameworkInstaller',
+ 'codeigniter' => 'CodeIgniterInstaller',
+ 'concrete5' => 'Concrete5Installer',
+ 'craft' => 'CraftInstaller',
+ 'croogo' => 'CroogoInstaller',
+ 'dokuwiki' => 'DokuWikiInstaller',
+ 'dolibarr' => 'DolibarrInstaller',
+ 'drupal' => 'DrupalInstaller',
+ 'elgg' => 'ElggInstaller',
+ 'fuel' => 'FuelInstaller',
+ 'fuelphp' => 'FuelphpInstaller',
+ 'grav' => 'GravInstaller',
+ 'hurad' => 'HuradInstaller',
+ 'joomla' => 'JoomlaInstaller',
+ 'kirby' => 'KirbyInstaller',
+ 'kohana' => 'KohanaInstaller',
+ 'laravel' => 'LaravelInstaller',
+ 'lithium' => 'LithiumInstaller',
+ 'magento' => 'MagentoInstaller',
+ 'mako' => 'MakoInstaller',
+ 'mediawiki' => 'MediaWikiInstaller',
+ 'microweber' => 'MicroweberInstaller',
+ 'modulework' => 'MODULEWorkInstaller',
+ 'modxevo' => 'MODXEvoInstaller',
+ 'moodle' => 'MoodleInstaller',
+ 'october' => 'OctoberInstaller',
+ 'oxid' => 'OxidInstaller',
+ 'phpbb' => 'PhpBBInstaller',
+ 'pimcore' => 'PimcoreInstaller',
+ 'piwik' => 'PiwikInstaller',
+ 'ppi' => 'PPIInstaller',
+ 'puppet' => 'PuppetInstaller',
+ 'redaxo' => 'RedaxoInstaller',
+ 'roundcube' => 'RoundcubeInstaller',
+ 'shopware' => 'ShopwareInstaller',
+ 'silverstripe' => 'SilverStripeInstaller',
+ 'smf' => 'SMFInstaller',
+ 'symfony1' => 'Symfony1Installer',
+ 'thelia' => 'TheliaInstaller',
+ 'tusk' => 'TuskInstaller',
+ 'typo3-cms' => 'TYPO3CmsInstaller',
+ 'typo3-flow' => 'TYPO3FlowInstaller',
+ 'whmcs' => 'WHMCSInstaller',
+ 'wolfcms' => 'WolfCMSInstaller',
+ 'wordpress' => 'WordPressInstaller',
+ 'zend' => 'ZendInstaller',
+ 'zikula' => 'ZikulaInstaller',
+ 'prestashop' => 'PrestashopInstaller',
+ );
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getInstallPath(PackageInterface $package)
+ {
+ $type = $package->getType();
+ $frameworkType = $this->findFrameworkType($type);
+
+ if ($frameworkType === false) {
+ throw new \InvalidArgumentException(
+ 'Sorry the package type of this package is not yet supported.'
+ );
+ }
+
+ $class = 'Composer\\Installers\\' . $this->supportedTypes[$frameworkType];
+ $installer = new $class($package, $this->composer);
+
+ return $installer->getInstallPath($package, $frameworkType);
+ }
+
+ public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
+ {
+ if (!$repo->hasPackage($package)) {
+ throw new \InvalidArgumentException('Package is not installed: '.$package);
+ }
+
+ $repo->removePackage($package);
+
+ $installPath = $this->getInstallPath($package);
+ $this->io->write(sprintf('Deleting %s - %s', $installPath, $this->filesystem->removeDirectory($installPath) ? 'deleted ' : 'not deleted '));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function supports($packageType)
+ {
+ $frameworkType = $this->findFrameworkType($packageType);
+
+ if ($frameworkType === false) {
+ return false;
+ }
+
+ $locationPattern = $this->getLocationPattern($frameworkType);
+
+ return preg_match('#' . $frameworkType . '-' . $locationPattern . '#', $packageType, $matches) === 1;
+ }
+
+ /**
+ * Finds a supported framework type if it exists and returns it
+ *
+ * @param string $type
+ * @return string
+ */
+ protected function findFrameworkType($type)
+ {
+ $frameworkType = false;
+
+ krsort($this->supportedTypes);
+
+ foreach ($this->supportedTypes as $key => $val) {
+ if ($key === substr($type, 0, strlen($key))) {
+ $frameworkType = substr($type, 0, strlen($key));
+ break;
+ }
+ }
+
+ return $frameworkType;
+ }
+
+ /**
+ * Get the second part of the regular expression to check for support of a
+ * package type
+ *
+ * @param string $frameworkType
+ * @return string
+ */
+ protected function getLocationPattern($frameworkType)
+ {
+ $pattern = false;
+ if (!empty($this->supportedTypes[$frameworkType])) {
+ $frameworkClass = 'Composer\\Installers\\' . $this->supportedTypes[$frameworkType];
+ /** @var BaseInstaller $framework */
+ $framework = new $frameworkClass(null, $this->composer);
+ $locations = array_keys($framework->getLocations());
+ $pattern = $locations ? '(' . implode('|', $locations) . ')' : false;
+ }
+
+ return $pattern ? : '(\w+)';
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/JoomlaInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/JoomlaInstaller.php
new file mode 100644
index 000000000..9ee775965
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/JoomlaInstaller.php
@@ -0,0 +1,15 @@
+ 'components/{$name}/',
+ 'module' => 'modules/{$name}/',
+ 'template' => 'templates/{$name}/',
+ 'plugin' => 'plugins/{$name}/',
+ 'library' => 'libraries/{$name}/',
+ );
+
+ // TODO: Add inflector for mod_ and com_ names
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/KirbyInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/KirbyInstaller.php
new file mode 100644
index 000000000..ae7ba8a4b
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/KirbyInstaller.php
@@ -0,0 +1,9 @@
+ 'site/plugins/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/KohanaInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/KohanaInstaller.php
new file mode 100644
index 000000000..dcd6d2632
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/KohanaInstaller.php
@@ -0,0 +1,9 @@
+ 'modules/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/LaravelInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/LaravelInstaller.php
new file mode 100644
index 000000000..be4d53a7b
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/LaravelInstaller.php
@@ -0,0 +1,9 @@
+ 'libraries/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/LithiumInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/LithiumInstaller.php
new file mode 100644
index 000000000..47bbd4cab
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/LithiumInstaller.php
@@ -0,0 +1,10 @@
+ 'libraries/{$name}/',
+ 'source' => 'libraries/_source/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php
new file mode 100644
index 000000000..9c2e9fb40
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php
@@ -0,0 +1,9 @@
+ 'modules/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MODXEvoInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MODXEvoInstaller.php
new file mode 100644
index 000000000..5a664608d
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MODXEvoInstaller.php
@@ -0,0 +1,16 @@
+ 'assets/snippets/{$name}/',
+ 'plugin' => 'assets/plugins/{$name}/',
+ 'module' => 'assets/modules/{$name}/',
+ 'template' => 'assets/templates/{$name}/',
+ 'lib' => 'assets/lib/{$name}/'
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MagentoInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MagentoInstaller.php
new file mode 100644
index 000000000..cf18e9478
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MagentoInstaller.php
@@ -0,0 +1,11 @@
+ 'app/design/frontend/{$name}/',
+ 'skin' => 'skin/frontend/default/{$name}/',
+ 'library' => 'lib/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MakoInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MakoInstaller.php
new file mode 100644
index 000000000..ca3cfacb4
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MakoInstaller.php
@@ -0,0 +1,9 @@
+ 'app/packages/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MediaWikiInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MediaWikiInstaller.php
new file mode 100644
index 000000000..01008c638
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MediaWikiInstaller.php
@@ -0,0 +1,50 @@
+ 'extensions/{$name}/',
+ 'skin' => 'skins/{$name}/',
+ );
+
+ /**
+ * Format package name.
+ *
+ * For package type mediawiki-extension, cut off a trailing '-extension' if present and transform
+ * to CamelCase keeping existing uppercase chars.
+ *
+ * For package type mediawiki-skin, cut off a trailing '-skin' if present.
+ *
+ */
+ public function inflectPackageVars($vars)
+ {
+
+ if ($vars['type'] === 'mediawiki-extension') {
+ return $this->inflectExtensionVars($vars);
+ }
+
+ if ($vars['type'] === 'mediawiki-skin') {
+ return $this->inflectSkinVars($vars);
+ }
+
+ return $vars;
+ }
+
+ protected function inflectExtensionVars($vars)
+ {
+ $vars['name'] = preg_replace('/-extension$/', '', $vars['name']);
+ $vars['name'] = str_replace('-', ' ', $vars['name']);
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
+
+ return $vars;
+ }
+
+ protected function inflectSkinVars($vars)
+ {
+ $vars['name'] = preg_replace('/-skin$/', '', $vars['name']);
+
+ return $vars;
+ }
+
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php
new file mode 100644
index 000000000..4bbbec8c0
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php
@@ -0,0 +1,111 @@
+ 'userfiles/modules/{$name}/',
+ 'module-skin' => 'userfiles/modules/{$name}/templates/',
+ 'template' => 'userfiles/templates/{$name}/',
+ 'element' => 'userfiles/elements/{$name}/',
+ 'vendor' => 'vendor/{$name}/',
+ 'components' => 'components/{$name}/'
+ );
+
+ /**
+ * Format package name.
+ *
+ * For package type microweber-module, cut off a trailing '-module' if present
+ *
+ * For package type microweber-template, cut off a trailing '-template' if present.
+ *
+ */
+ public function inflectPackageVars($vars)
+ {
+ if ($vars['type'] === 'microweber-template') {
+ return $this->inflectTemplateVars($vars);
+ }
+ if ($vars['type'] === 'microweber-templates') {
+ return $this->inflectTemplatesVars($vars);
+ }
+ if ($vars['type'] === 'microweber-core') {
+ return $this->inflectCoreVars($vars);
+ }
+ if ($vars['type'] === 'microweber-adapter') {
+ return $this->inflectCoreVars($vars);
+ }
+ if ($vars['type'] === 'microweber-module') {
+ return $this->inflectModuleVars($vars);
+ }
+ if ($vars['type'] === 'microweber-modules') {
+ return $this->inflectModulesVars($vars);
+ }
+ if ($vars['type'] === 'microweber-skin') {
+ return $this->inflectSkinVars($vars);
+ }
+ if ($vars['type'] === 'microweber-element' or $vars['type'] === 'microweber-elements') {
+ return $this->inflectElementVars($vars);
+ }
+
+ return $vars;
+ }
+
+ protected function inflectTemplateVars($vars)
+ {
+ $vars['name'] = preg_replace('/-template$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/template-$/', '', $vars['name']);
+
+ return $vars;
+ }
+
+ protected function inflectTemplatesVars($vars)
+ {
+ $vars['name'] = preg_replace('/-templates$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/templates-$/', '', $vars['name']);
+
+ return $vars;
+ }
+
+ protected function inflectCoreVars($vars)
+ {
+ $vars['name'] = preg_replace('/-providers$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/-provider$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/-adapter$/', '', $vars['name']);
+
+ return $vars;
+ }
+
+ protected function inflectModuleVars($vars)
+ {
+ $vars['name'] = preg_replace('/-module$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/module-$/', '', $vars['name']);
+
+ return $vars;
+ }
+
+ protected function inflectModulesVars($vars)
+ {
+ $vars['name'] = preg_replace('/-modules$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/modules-$/', '', $vars['name']);
+
+ return $vars;
+ }
+
+ protected function inflectSkinVars($vars)
+ {
+ $vars['name'] = preg_replace('/-skin$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/skin-$/', '', $vars['name']);
+
+ return $vars;
+ }
+
+ protected function inflectElementVars($vars)
+ {
+ $vars['name'] = preg_replace('/-elements$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/elements-$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/-element$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/element-$/', '', $vars['name']);
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php
new file mode 100644
index 000000000..04be73c2a
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php
@@ -0,0 +1,47 @@
+ 'mod/{$name}/',
+ 'admin_report' => 'admin/report/{$name}/',
+ 'tool' => 'admin/tool/{$name}/',
+ 'assignment' => 'mod/assignment/type/{$name}/',
+ 'assignsubmission' => 'mod/assign/submission/{$name}/',
+ 'assignfeedback' => 'mod/assign/feedback/{$name}/',
+ 'auth' => 'auth/{$name}/',
+ 'availability' => 'availability/condition/{$name}/',
+ 'block' => 'blocks/{$name}/',
+ 'calendartype' => 'calendar/type/{$name}/',
+ 'format' => 'course/format/{$name}/',
+ 'coursereport' => 'course/report/{$name}/',
+ 'datafield' => 'mod/data/field/{$name}/',
+ 'datapreset' => 'mod/data/preset/{$name}/',
+ 'editor' => 'lib/editor/{$name}/',
+ 'enrol' => 'enrol/{$name}/',
+ 'filter' => 'filter/{$name}/',
+ 'gradeexport' => 'grade/export/{$name}/',
+ 'gradeimport' => 'grade/import/{$name}/',
+ 'gradereport' => 'grade/report/{$name}/',
+ 'gradingform' => 'grade/grading/form/{$name}/',
+ 'local' => 'local/{$name}/',
+ 'message' => 'message/output/{$name}/',
+ 'plagiarism' => 'plagiarism/{$name}/',
+ 'portfolio' => 'portfolio/{$name}/',
+ 'qbehaviour' => 'question/behaviour/{$name}/',
+ 'qformat' => 'question/format/{$name}/',
+ 'qtype' => 'question/type/{$name}/',
+ 'quizaccess' => 'mod/quiz/accessrule/{$name}/',
+ 'quiz' => 'mod/quiz/report/{$name}/',
+ 'report' => 'report/{$name}/',
+ 'repository' => 'repository/{$name}/',
+ 'scormreport' => 'mod/scorm/report/{$name}/',
+ 'theme' => 'theme/{$name}/',
+ 'profilefield' => 'user/profile/field/{$name}/',
+ 'webservice' => 'webservice/{$name}/',
+ 'workshopallocation' => 'mod/workshop/allocation/{$name}/',
+ 'workshopeval' => 'mod/workshop/eval/{$name}/',
+ 'workshopform' => 'mod/workshop/form/{$name}/'
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/OctoberInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/OctoberInstaller.php
new file mode 100644
index 000000000..6bf53fd14
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/OctoberInstaller.php
@@ -0,0 +1,46 @@
+ 'modules/{$name}/',
+ 'plugin' => 'plugins/{$vendor}/{$name}/',
+ 'theme' => 'themes/{$name}/'
+ );
+
+ /**
+ * Format package name.
+ *
+ * For package type october-plugin, cut off a trailing '-plugin' if present.
+ *
+ * For package type october-theme, cut off a trailing '-theme' if present.
+ *
+ */
+ public function inflectPackageVars($vars)
+ {
+ if ($vars['type'] === 'october-plugin') {
+ return $this->inflectPluginVars($vars);
+ }
+
+ if ($vars['type'] === 'october-theme') {
+ return $this->inflectThemeVars($vars);
+ }
+
+ return $vars;
+ }
+
+ protected function inflectPluginVars($vars)
+ {
+ $vars['name'] = preg_replace('/-plugin$/', '', $vars['name']);
+
+ return $vars;
+ }
+
+ protected function inflectThemeVars($vars)
+ {
+ $vars['name'] = preg_replace('/-theme$/', '', $vars['name']);
+
+ return $vars;
+ }
+}
\ No newline at end of file
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/OxidInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/OxidInstaller.php
new file mode 100644
index 000000000..22fb56aa1
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/OxidInstaller.php
@@ -0,0 +1,11 @@
+ 'modules/{$name}/',
+ 'theme' => 'application/views/{$name}/',
+ 'out' => 'out/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PPIInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PPIInstaller.php
new file mode 100644
index 000000000..170136f98
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PPIInstaller.php
@@ -0,0 +1,9 @@
+ 'modules/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PhpBBInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PhpBBInstaller.php
new file mode 100644
index 000000000..deb2b77a6
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PhpBBInstaller.php
@@ -0,0 +1,11 @@
+ 'ext/{$vendor}/{$name}/',
+ 'language' => 'language/{$name}/',
+ 'style' => 'styles/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PimcoreInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PimcoreInstaller.php
new file mode 100644
index 000000000..4781fa6d1
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PimcoreInstaller.php
@@ -0,0 +1,21 @@
+ 'plugins/{$name}/',
+ );
+
+ /**
+ * Format package name to CamelCase
+ */
+ public function inflectPackageVars($vars)
+ {
+ $vars['name'] = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name']));
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PiwikInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PiwikInstaller.php
new file mode 100644
index 000000000..c17f4572b
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PiwikInstaller.php
@@ -0,0 +1,32 @@
+ 'plugins/{$name}/',
+ );
+
+ /**
+ * Format package name to CamelCase
+ * @param array $vars
+ *
+ * @return array
+ */
+ public function inflectPackageVars($vars)
+ {
+ $vars['name'] = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name']));
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PrestashopInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PrestashopInstaller.php
new file mode 100644
index 000000000..4c8421e36
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PrestashopInstaller.php
@@ -0,0 +1,10 @@
+ 'modules/{$name}/',
+ 'theme' => 'themes/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PuppetInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PuppetInstaller.php
new file mode 100644
index 000000000..77cc3dd87
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PuppetInstaller.php
@@ -0,0 +1,11 @@
+ 'modules/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/RedaxoInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/RedaxoInstaller.php
new file mode 100644
index 000000000..09544576b
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/RedaxoInstaller.php
@@ -0,0 +1,10 @@
+ 'redaxo/include/addons/{$name}/',
+ 'bestyle-plugin' => 'redaxo/include/addons/be_style/plugins/{$name}/'
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/RoundcubeInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/RoundcubeInstaller.php
new file mode 100644
index 000000000..d8d795be0
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/RoundcubeInstaller.php
@@ -0,0 +1,22 @@
+ 'plugins/{$name}/',
+ );
+
+ /**
+ * Lowercase name and changes the name to a underscores
+ *
+ * @param array $vars
+ * @return array
+ */
+ public function inflectPackageVars($vars)
+ {
+ $vars['name'] = strtolower(str_replace('-', '_', $vars['name']));
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/SMFInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/SMFInstaller.php
new file mode 100644
index 000000000..1acd3b14c
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/SMFInstaller.php
@@ -0,0 +1,10 @@
+ 'Sources/{$name}/',
+ 'theme' => 'Themes/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ShopwareInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ShopwareInstaller.php
new file mode 100644
index 000000000..673f1fc1f
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ShopwareInstaller.php
@@ -0,0 +1,58 @@
+ 'engine/Shopware/Plugins/Local/Backend/{$name}/',
+ 'core-plugin' => 'engine/Shopware/Plugins/Local/Core/{$name}/',
+ 'frontend-plugin' => 'engine/Shopware/Plugins/Local/Frontend/{$name}/',
+ 'theme' => 'templates/{$name}/'
+ );
+
+ /**
+ * Transforms the names
+ * @param array $vars
+ * @return array
+ */
+ public function inflectPackageVars($vars)
+ {
+ if ($vars['type'] === 'shopware-theme') {
+ return $this->correctThemeName($vars);
+ } else {
+ return $this->correctPluginName($vars);
+ }
+ }
+
+ /**
+ * Changes the name to a camelcased combination of vendor and name
+ * @param array $vars
+ * @return array
+ */
+ private function correctPluginName($vars)
+ {
+ $camelCasedName = preg_replace_callback('/(-[a-z])/', function ($matches) {
+ return strtoupper($matches[0][1]);
+ }, $vars['name']);
+
+ $vars['name'] = ucfirst($vars['vendor']) . ucfirst($camelCasedName);
+
+ return $vars;
+ }
+
+ /**
+ * Changes the name to a underscore separated name
+ * @param array $vars
+ * @return array
+ */
+ private function correctThemeName($vars)
+ {
+ $vars['name'] = str_replace('-', '_', $vars['name']);
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/SilverStripeInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/SilverStripeInstaller.php
new file mode 100644
index 000000000..17ca543a2
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/SilverStripeInstaller.php
@@ -0,0 +1,36 @@
+ '{$name}/',
+ 'theme' => 'themes/{$name}/',
+ );
+
+ /**
+ * Return the install path based on package type.
+ *
+ * Relies on built-in BaseInstaller behaviour with one exception: silverstripe/framework
+ * must be installed to 'sapphire' and not 'framework' if the version is <3.0.0
+ *
+ * @param PackageInterface $package
+ * @param string $frameworkType
+ * @return string
+ */
+ public function getInstallPath(PackageInterface $package, $frameworkType = '')
+ {
+ if (
+ $package->getName() == 'silverstripe/framework'
+ && preg_match('/^\d+\.\d+\.\d+/', $package->getVersion())
+ && version_compare($package->getVersion(), '2.999.999') < 0
+ ) {
+ return $this->templatePath($this->locations['module'], array('name' => 'sapphire'));
+ } else {
+ return parent::getInstallPath($package, $frameworkType);
+ }
+
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/Symfony1Installer.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/Symfony1Installer.php
new file mode 100644
index 000000000..1675c4f21
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/Symfony1Installer.php
@@ -0,0 +1,26 @@
+
+ */
+class Symfony1Installer extends BaseInstaller
+{
+ protected $locations = array(
+ 'plugin' => 'plugins/{$name}/',
+ );
+
+ /**
+ * Format package name to CamelCase
+ */
+ public function inflectPackageVars($vars)
+ {
+ $vars['name'] = preg_replace_callback('/(-[a-z])/', function ($matches) {
+ return strtoupper($matches[0][1]);
+ }, $vars['name']);
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php
new file mode 100644
index 000000000..8220b40df
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php
@@ -0,0 +1,14 @@
+
+ */
+class TYPO3CmsInstaller extends BaseInstaller
+{
+ protected $locations = array(
+ 'extension' => 'typo3conf/ext/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3FlowInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3FlowInstaller.php
new file mode 100644
index 000000000..42572f44f
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3FlowInstaller.php
@@ -0,0 +1,38 @@
+ 'Packages/Application/{$name}/',
+ 'framework' => 'Packages/Framework/{$name}/',
+ 'plugin' => 'Packages/Plugins/{$name}/',
+ 'site' => 'Packages/Sites/{$name}/',
+ 'boilerplate' => 'Packages/Boilerplates/{$name}/',
+ 'build' => 'Build/{$name}/',
+ );
+
+ /**
+ * Modify the package name to be a TYPO3 Flow style key.
+ *
+ * @param array $vars
+ * @return array
+ */
+ public function inflectPackageVars($vars)
+ {
+ $autoload = $this->package->getAutoload();
+ if (isset($autoload['psr-0']) && is_array($autoload['psr-0'])) {
+ $namespace = key($autoload['psr-0']);
+ $vars['name'] = str_replace('\\', '.', $namespace);
+ }
+ if (isset($autoload['psr-4']) && is_array($autoload['psr-4'])) {
+ $namespace = key($autoload['psr-4']);
+ $vars['name'] = rtrim(str_replace('\\', '.', $namespace), '.');
+ }
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/TheliaInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/TheliaInstaller.php
new file mode 100644
index 000000000..158af5261
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/TheliaInstaller.php
@@ -0,0 +1,12 @@
+ 'local/modules/{$name}/',
+ 'frontoffice-template' => 'templates/frontOffice/{$name}/',
+ 'backoffice-template' => 'templates/backOffice/{$name}/',
+ 'email-template' => 'templates/email/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/TuskInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/TuskInstaller.php
new file mode 100644
index 000000000..7c0113b85
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/TuskInstaller.php
@@ -0,0 +1,14 @@
+
+ */
+ class TuskInstaller extends BaseInstaller
+ {
+ protected $locations = array(
+ 'task' => '.tusk/tasks/{$name}/',
+ 'command' => '.tusk/commands/{$name}/',
+ 'asset' => 'assets/tusk/{$name}/',
+ );
+ }
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php
new file mode 100644
index 000000000..2cbb4a463
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php
@@ -0,0 +1,10 @@
+ 'modules/gateways/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/WolfCMSInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/WolfCMSInstaller.php
new file mode 100644
index 000000000..cb387881d
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/WolfCMSInstaller.php
@@ -0,0 +1,9 @@
+ 'wolf/plugins/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/WordPressInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/WordPressInstaller.php
new file mode 100644
index 000000000..b03219c69
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/WordPressInstaller.php
@@ -0,0 +1,11 @@
+ 'wp-content/plugins/{$name}/',
+ 'theme' => 'wp-content/themes/{$name}/',
+ 'muplugin' => 'wp-content/mu-plugins/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ZendInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ZendInstaller.php
new file mode 100644
index 000000000..bde9bc8c8
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ZendInstaller.php
@@ -0,0 +1,11 @@
+ 'library/{$name}/',
+ 'extra' => 'extras/library/{$name}/',
+ 'module' => 'module/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ZikulaInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ZikulaInstaller.php
new file mode 100644
index 000000000..56cdf5da7
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ZikulaInstaller.php
@@ -0,0 +1,10 @@
+ 'modules/{$vendor}-{$name}/',
+ 'theme' => 'themes/{$vendor}-{$name}/'
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/bootstrap.php b/web/api/app/vendor/composer/installers/src/bootstrap.php
new file mode 100644
index 000000000..0de276ee2
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/bootstrap.php
@@ -0,0 +1,13 @@
+installer = new AsgardInstaller(
+ new Package('NyanCat', '4.2', '4.2'),
+ new Composer()
+ );
+ }
+
+ /**
+ * @dataProvider packageNameInflectionProvider
+ */
+ public function testInflectPackageVars($type, $name, $expected)
+ {
+ $this->assertEquals(
+ $this->installer->inflectPackageVars(array('name' => $name, 'type' => $type)),
+ array('name' => $expected, 'type' => $type)
+ );
+ }
+
+ public function packageNameInflectionProvider()
+ {
+ return array(
+ array(
+ 'asgard-module',
+ 'asgard-module',
+ 'Asgard'
+ ),
+ array(
+ 'asgard-module',
+ 'blog',
+ 'Blog'
+ ),
+ // tests that exactly one '-theme' is cut off
+ array(
+ 'asgard-theme',
+ 'some-theme-theme',
+ 'Some-theme',
+ ),
+ // tests that names without '-theme' suffix stay valid
+ array(
+ 'asgard-theme',
+ 'someothertheme',
+ 'Someothertheme',
+ ),
+ );
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/CakePHPInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/CakePHPInstallerTest.php
new file mode 100644
index 000000000..976bd9be8
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/CakePHPInstallerTest.php
@@ -0,0 +1,115 @@
+package = new Package('CamelCased', '1.0', '1.0');
+ $this->io = $this->getMock('Composer\IO\PackageInterface');
+ $this->composer = new Composer();
+ $this->composer->setConfig(new Config(false));
+ }
+
+ /**
+ * testInflectPackageVars
+ *
+ * @return void
+ */
+ public function testInflectPackageVars()
+ {
+ $installer = new CakePHPInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'CamelCased'));
+ $this->assertEquals($result, array('name' => 'CamelCased'));
+
+ $installer = new CakePHPInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'with-dash'));
+ $this->assertEquals($result, array('name' => 'WithDash'));
+
+ $installer = new CakePHPInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'with_underscore'));
+ $this->assertEquals($result, array('name' => 'WithUnderscore'));
+
+ $installer = new CakePHPInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'cake/acl'));
+ $this->assertEquals($result, array('name' => 'Cake/Acl'));
+
+ $installer = new CakePHPInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'cake/debug-kit'));
+ $this->assertEquals($result, array('name' => 'Cake/DebugKit'));
+ }
+
+ /**
+ * Test getLocations returning appropriate values based on CakePHP version
+ *
+ */
+ public function testGetLocations() {
+ $package = new RootPackage('CamelCased', '1.0', '1.0');
+ $composer = $this->composer;
+ $rm = new RepositoryManager(
+ $this->getMock('Composer\IO\IOInterface'),
+ $this->getMock('Composer\Config')
+ );
+ $composer->setRepositoryManager($rm);
+ $installer = new CakePHPInstaller($package, $composer);
+
+ // 2.0 < cakephp < 3.0
+ $this->setCakephpVersion($rm, '2.0.0');
+ $result = $installer->getLocations();
+ $this->assertContains('Plugin/', $result['plugin']);
+
+ $this->setCakephpVersion($rm, '2.5.9');
+ $result = $installer->getLocations();
+ $this->assertContains('Plugin/', $result['plugin']);
+
+ $this->setCakephpVersion($rm, '~2.5');
+ $result = $installer->getLocations();
+ $this->assertContains('Plugin/', $result['plugin']);
+
+ // special handling for 2.x versions when 3.x is still in development
+ $this->setCakephpVersion($rm, 'dev-master');
+ $result = $installer->getLocations();
+ $this->assertContains('Plugin/', $result['plugin']);
+
+ $this->setCakephpVersion($rm, '>=2.5');
+ $result = $installer->getLocations();
+ $this->assertContains('Plugin/', $result['plugin']);
+
+ // cakephp >= 3.0
+ $this->setCakephpVersion($rm, '3.0.*-dev');
+ $result = $installer->getLocations();
+ $this->assertContains('vendor/{$vendor}/{$name}/', $result['plugin']);
+
+ $this->setCakephpVersion($rm, '~8.8');
+ $result = $installer->getLocations();
+ $this->assertEquals('vendor/{$vendor}/{$name}/', $result['plugin']);
+ }
+
+ protected function setCakephpVersion($rm, $version) {
+ $parser = new VersionParser();
+ list(, $version) = explode(' ', $parser->parseConstraints($version));
+ $installed = new InstalledArrayRepository();
+ $package = new Package('cakephp/cakephp', $version, $version);
+ $installed->addPackage($package);
+ $rm->setLocalRepository($installed);
+ }
+
+}
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/DokuWikiInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/DokuWikiInstallerTest.php
new file mode 100644
index 000000000..9e385e6a8
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/DokuWikiInstallerTest.php
@@ -0,0 +1,89 @@
+installer = new DokuWikiInstaller(
+ new Package('NyanCat', '4.2', '4.2'),
+ new Composer()
+ );
+ }
+
+ /**
+ * @dataProvider packageNameInflectionProvider
+ */
+ public function testInflectPackageVars($type, $name, $expected)
+ {
+ $this->assertEquals(
+ $this->installer->inflectPackageVars(array('name' => $name, 'type'=>$type)),
+ array('name' => $expected, 'type'=>$type)
+ );
+ }
+
+ public function packageNameInflectionProvider()
+ {
+ return array(
+ array(
+ 'dokuwiki-plugin',
+ 'dokuwiki-test-plugin',
+ 'test',
+ ),
+ array(
+ 'dokuwiki-plugin',
+ 'test-plugin',
+ 'test',
+ ),
+ array(
+ 'dokuwiki-plugin',
+ 'dokuwiki_test',
+ 'test',
+ ),
+ array(
+ 'dokuwiki-plugin',
+ 'test',
+ 'test',
+ ),
+ array(
+ 'dokuwiki-plugin',
+ 'test-template',
+ 'test-template',
+ ),
+ array(
+ 'dokuwiki-template',
+ 'dokuwiki-test-template',
+ 'test',
+ ),
+ array(
+ 'dokuwiki-template',
+ 'test-template',
+ 'test',
+ ),
+ array(
+ 'dokuwiki-template',
+ 'dokuwiki_test',
+ 'test',
+ ),
+ array(
+ 'dokuwiki-template',
+ 'test',
+ 'test',
+ ),
+ array(
+ 'dokuwiki-template',
+ 'test-plugin',
+ 'test-plugin',
+ ),
+ );
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/GravInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/GravInstallerTest.php
new file mode 100644
index 000000000..b757799b4
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/GravInstallerTest.php
@@ -0,0 +1,63 @@
+composer = new Composer();
+ }
+
+ public function testInflectPackageVars()
+ {
+ $package = $this->getPackage('vendor/name', '0.0.0');
+ $installer = new GravInstaller($package, $this->composer);
+ $packageVars = $this->getPackageVars($package);
+
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => 'test')));
+ $this->assertEquals('test', $result['name']);
+
+ foreach ($installer->getLocations() as $name => $location) {
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "$name-test")));
+ $this->assertEquals('test', $result['name']);
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "test-$name")));
+ $this->assertEquals('test', $result['name']);
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "$name-test-test")));
+ $this->assertEquals('test-test', $result['name']);
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "test-test-$name")));
+ $this->assertEquals('test-test', $result['name']);
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "grav-$name-test")));
+ $this->assertEquals('test', $result['name']);
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "grav-test-$name")));
+ $this->assertEquals('test', $result['name']);
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "grav-$name-test-test")));
+ $this->assertEquals('test-test', $result['name']);
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "grav-test-test-$name")));
+ $this->assertEquals('test-test', $result['name']);
+ }
+ }
+
+ /**
+ * @param $package \Composer\Package\PackageInterface
+ */
+ public function getPackageVars($package)
+ {
+ $type = $package->getType();
+
+ $prettyName = $package->getPrettyName();
+ if (strpos($prettyName, '/') !== false) {
+ list($vendor, $name) = explode('/', $prettyName);
+ } else {
+ $vendor = '';
+ $name = $prettyName;
+ }
+
+ return compact('name', 'vendor', 'type');
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/InstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/InstallerTest.php
new file mode 100644
index 000000000..a516daf07
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/InstallerTest.php
@@ -0,0 +1,422 @@
+fs = new Filesystem;
+
+ $this->composer = new Composer();
+ $this->config = new Config();
+ $this->composer->setConfig($this->config);
+
+ $this->vendorDir = realpath(sys_get_temp_dir()) . DIRECTORY_SEPARATOR . 'baton-test-vendor';
+ $this->ensureDirectoryExistsAndClear($this->vendorDir);
+
+ $this->binDir = realpath(sys_get_temp_dir()) . DIRECTORY_SEPARATOR . 'baton-test-bin';
+ $this->ensureDirectoryExistsAndClear($this->binDir);
+
+ $this->config->merge(array(
+ 'config' => array(
+ 'vendor-dir' => $this->vendorDir,
+ 'bin-dir' => $this->binDir,
+ ),
+ ));
+
+ $this->dm = $this->getMockBuilder('Composer\Downloader\DownloadManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->composer->setDownloadManager($this->dm);
+
+ $this->repository = $this->getMock('Composer\Repository\InstalledRepositoryInterface');
+ $this->io = $this->getMock('Composer\IO\IOInterface');
+ }
+
+ /**
+ * tearDown
+ *
+ * @return void
+ */
+ public function tearDown()
+ {
+ $this->fs->removeDirectory($this->vendorDir);
+ $this->fs->removeDirectory($this->binDir);
+ }
+
+ /**
+ * testSupports
+ *
+ * @return void
+ *
+ * @dataProvider dataForTestSupport
+ */
+ public function testSupports($type, $expected)
+ {
+ $installer = new Installer($this->io, $this->composer);
+ $this->assertSame($expected, $installer->supports($type), sprintf('Failed to show support for %s', $type));
+ }
+
+ /**
+ * dataForTestSupport
+ */
+ public function dataForTestSupport()
+ {
+ return array(
+ array('agl-module', true),
+ array('aimeos-extension', true),
+ array('annotatecms-module', true),
+ array('annotatecms-component', true),
+ array('annotatecms-service', true),
+ array('bitrix-module', true),
+ array('bitrix-component', true),
+ array('bitrix-theme', true),
+ array('cakephp', false),
+ array('cakephp-', false),
+ array('cakephp-app', false),
+ array('cakephp-plugin', true),
+ array('chef-cookbook', true),
+ array('chef-role', true),
+ array('codeigniter-app', false),
+ array('codeigniter-library', true),
+ array('codeigniter-third-party', true),
+ array('codeigniter-module', true),
+ array('concrete5-block', true),
+ array('concrete5-package', true),
+ array('concrete5-theme', true),
+ array('concrete5-update', true),
+ array('craft-plugin', true),
+ array('croogo-plugin', true),
+ array('croogo-theme', true),
+ array('dokuwiki-plugin', true),
+ array('dokuwiki-template', true),
+ array('drupal-module', true),
+ array('dolibarr-module', true),
+ array('elgg-plugin', true),
+ array('fuel-module', true),
+ array('fuel-package', true),
+ array('fuel-theme', true),
+ array('fuelphp-component', true),
+ array('hurad-plugin', true),
+ array('hurad-theme', true),
+ array('joomla-library', true),
+ array('kirby-plugin', true),
+ array('kohana-module', true),
+ array('laravel-library', true),
+ array('lithium-library', true),
+ array('magento-library', true),
+ array('mako-package', true),
+ array('modxevo-snippet', true),
+ array('modxevo-plugin', true),
+ array('modxevo-module', true),
+ array('modxevo-template', true),
+ array('modxevo-lib', true),
+ array('mediawiki-extension', true),
+ array('mediawiki-skin', true),
+ array('microweber-module', true),
+ array('modulework-module', true),
+ array('moodle-mod', true),
+ array('october-module', true),
+ array('october-plugin', true),
+ array('piwik-plugin', true),
+ array('phpbb-extension', true),
+ array('pimcore-plugin', true),
+ array('ppi-module', true),
+ array('prestashop-module', true),
+ array('prestashop-theme', true),
+ array('puppet-module', true),
+ array('redaxo-addon', true),
+ array('redaxo-bestyle-plugin', true),
+ array('roundcube-plugin', true),
+ array('shopware-backend-plugin', true),
+ array('shopware-core-plugin', true),
+ array('shopware-frontend-plugin', true),
+ array('shopware-theme', true),
+ array('silverstripe-module', true),
+ array('silverstripe-theme', true),
+ array('smf-module', true),
+ array('smf-theme', true),
+ array('symfony1-plugin', true),
+ array('thelia-module', true),
+ array('thelia-frontoffice-template', true),
+ array('thelia-backoffice-template', true),
+ array('thelia-email-template', true),
+ array('tusk-task', true),
+ array('tusk-asset', true),
+ array('typo3-flow-plugin', true),
+ array('typo3-cms-extension', true),
+ array('whmcs-gateway', true),
+ array('wolfcms-plugin', true),
+ array('wordpress-plugin', true),
+ array('wordpress-core', false),
+ array('zend-library', true),
+ array('zikula-module', true),
+ array('zikula-theme', true),
+ );
+ }
+
+ /**
+ * testInstallPath
+ *
+ * @dataProvider dataForTestInstallPath
+ */
+ public function testInstallPath($type, $path, $name, $version = '1.0.0')
+ {
+ $installer = new Installer($this->io, $this->composer);
+ $package = new Package($name, $version, $version);
+
+ $package->setType($type);
+ $result = $installer->getInstallPath($package);
+ $this->assertEquals($path, $result);
+ }
+
+ /**
+ * dataFormTestInstallPath
+ */
+ public function dataForTestInstallPath()
+ {
+ return array(
+ array('agl-module', 'More/MyTestPackage/', 'agl/my_test-package'),
+ array('aimeos-extension', 'ext/ai-test/', 'author/ai-test'),
+ array('annotatecms-module', 'addons/modules/my_module/', 'vysinsky/my_module'),
+ array('annotatecms-component', 'addons/components/my_component/', 'vysinsky/my_component'),
+ array('annotatecms-service', 'addons/services/my_service/', 'vysinsky/my_service'),
+ array('bitrix-module', 'local/modules/my_module/', 'author/my_module'),
+ array('bitrix-component', 'local/components/my_component/', 'author/my_component'),
+ array('bitrix-theme', 'local/templates/my_theme/', 'author/my_theme'),
+ array('cakephp-plugin', 'Plugin/Ftp/', 'shama/ftp'),
+ array('chef-cookbook', 'Chef/mre/my_cookbook/', 'mre/my_cookbook'),
+ array('chef-role', 'Chef/roles/my_role/', 'mre/my_role'),
+ array('codeigniter-library', 'application/libraries/my_package/', 'shama/my_package'),
+ array('codeigniter-module', 'application/modules/my_package/', 'shama/my_package'),
+ array('concrete5-block', 'blocks/concrete5_block/', 'remo/concrete5_block'),
+ array('concrete5-package', 'packages/concrete5_package/', 'remo/concrete5_package'),
+ array('concrete5-theme', 'themes/concrete5_theme/', 'remo/concrete5_theme'),
+ array('concrete5-update', 'updates/concrete5/', 'concrete5/concrete5'),
+ array('craft-plugin', 'craft/plugins/my_plugin/', 'mdcpepper/my_plugin'),
+ array('croogo-plugin', 'Plugin/Sitemaps/', 'fahad19/sitemaps'),
+ array('croogo-theme', 'View/Themed/Readable/', 'rchavik/readable'),
+ array('dokuwiki-plugin', 'lib/plugins/someplugin/', 'author/someplugin'),
+ array('dokuwiki-template', 'lib/tpl/sometemplate/', 'author/sometemplate'),
+ array('dolibarr-module', 'htdocs/custom/my_module/', 'shama/my_module'),
+ array('drupal-module', 'modules/my_module/', 'shama/my_module'),
+ array('drupal-theme', 'themes/my_module/', 'shama/my_module'),
+ array('drupal-profile', 'profiles/my_module/', 'shama/my_module'),
+ array('drupal-drush', 'drush/my_module/', 'shama/my_module'),
+ array('elgg-plugin', 'mod/sample_plugin/', 'test/sample_plugin'),
+ array('fuel-module', 'fuel/app/modules/module/', 'fuel/module'),
+ array('fuel-package', 'fuel/packages/orm/', 'fuel/orm'),
+ array('fuel-theme', 'fuel/app/themes/theme/', 'fuel/theme'),
+ array('fuelphp-component', 'components/demo/', 'fuelphp/demo'),
+ array('hurad-plugin', 'plugins/Akismet/', 'atkrad/akismet'),
+ array('hurad-theme', 'plugins/Hurad2013/', 'atkrad/Hurad2013'),
+ array('joomla-plugin', 'plugins/my_plugin/', 'shama/my_plugin'),
+ array('kirby-plugin', 'site/plugins/my_plugin/', 'shama/my_plugin'),
+ array('kohana-module', 'modules/my_package/', 'shama/my_package'),
+ array('laravel-library', 'libraries/my_package/', 'shama/my_package'),
+ array('lithium-library', 'libraries/li3_test/', 'user/li3_test'),
+ array('magento-library', 'lib/foo/', 'test/foo'),
+ array('modxevo-snippet', 'assets/snippets/my_snippet/', 'shama/my_snippet'),
+ array('modxevo-plugin', 'assets/plugins/my_plugin/', 'shama/my_plugin'),
+ array('modxevo-module', 'assets/modules/my_module/', 'shama/my_module'),
+ array('modxevo-template', 'assets/templates/my_template/', 'shama/my_template'),
+ array('modxevo-lib', 'assets/lib/my_lib/', 'shama/my_lib'),
+ array('mako-package', 'app/packages/my_package/', 'shama/my_package'),
+ array('mediawiki-extension', 'extensions/APC/', 'author/APC'),
+ array('mediawiki-extension', 'extensions/APC/', 'author/APC-extension'),
+ array('mediawiki-extension', 'extensions/UploadWizard/', 'author/upload-wizard'),
+ array('mediawiki-extension', 'extensions/SyntaxHighlight_GeSHi/', 'author/syntax-highlight_GeSHi'),
+ array('mediawiki-skin', 'skins/someskin/', 'author/someskin-skin'),
+ array('mediawiki-skin', 'skins/someskin/', 'author/someskin'),
+ array('microweber-module', 'userfiles/modules/my-thing/', 'author/my-thing-module'),
+ array('modulework-module', 'modules/my_package/', 'shama/my_package'),
+ array('moodle-mod', 'mod/my_package/', 'shama/my_package'),
+ array('october-module', 'modules/my_plugin/', 'shama/my_plugin'),
+ array('october-plugin', 'plugins/shama/my_plugin/', 'shama/my_plugin'),
+ array('october-theme', 'themes/my_theme/', 'shama/my_theme'),
+ array('piwik-plugin', 'plugins/VisitSummary/', 'shama/visit-summary'),
+ array('prestashop-module', 'modules/a-module/', 'vendor/a-module'),
+ array('prestashop-theme', 'themes/a-theme/', 'vendor/a-theme'),
+ array('phpbb-extension', 'ext/test/foo/', 'test/foo'),
+ array('phpbb-style', 'styles/foo/', 'test/foo'),
+ array('phpbb-language', 'language/foo/', 'test/foo'),
+ array('pimcore-plugin', 'plugins/MyPlugin/', 'ubikz/my_plugin'),
+ array('ppi-module', 'modules/foo/', 'test/foo'),
+ array('puppet-module', 'modules/puppet-name/', 'puppet/puppet-name'),
+ array('redaxo-addon', 'redaxo/include/addons/my_plugin/', 'shama/my_plugin'),
+ array('redaxo-bestyle-plugin', 'redaxo/include/addons/be_style/plugins/my_plugin/', 'shama/my_plugin'),
+ array('roundcube-plugin', 'plugins/base/', 'test/base'),
+ array('roundcube-plugin', 'plugins/replace_dash/', 'test/replace-dash'),
+ array('shopware-backend-plugin', 'engine/Shopware/Plugins/Local/Backend/ShamaMyBackendPlugin/', 'shama/my-backend-plugin'),
+ array('shopware-core-plugin', 'engine/Shopware/Plugins/Local/Core/ShamaMyCorePlugin/', 'shama/my-core-plugin'),
+ array('shopware-frontend-plugin', 'engine/Shopware/Plugins/Local/Frontend/ShamaMyFrontendPlugin/', 'shama/my-frontend-plugin'),
+ array('shopware-theme', 'templates/my_theme/', 'shama/my-theme'),
+ array('silverstripe-module', 'my_module/', 'shama/my_module'),
+ array('silverstripe-module', 'sapphire/', 'silverstripe/framework', '2.4.0'),
+ array('silverstripe-module', 'framework/', 'silverstripe/framework', '3.0.0'),
+ array('silverstripe-module', 'framework/', 'silverstripe/framework', '3.0.0-rc1'),
+ array('silverstripe-module', 'framework/', 'silverstripe/framework', 'my/branch'),
+ array('silverstripe-theme', 'themes/my_theme/', 'shama/my_theme'),
+ array('smf-module', 'Sources/my_module/', 'shama/my_module'),
+ array('smf-theme', 'Themes/my_theme/', 'shama/my_theme'),
+ array('symfony1-plugin', 'plugins/sfShamaPlugin/', 'shama/sfShamaPlugin'),
+ array('symfony1-plugin', 'plugins/sfShamaPlugin/', 'shama/sf-shama-plugin'),
+ array('thelia-module', 'local/modules/my_module/', 'shama/my_module'),
+ array('thelia-frontoffice-template', 'templates/frontOffice/my_template_fo/', 'shama/my_template_fo'),
+ array('thelia-backoffice-template', 'templates/backOffice/my_template_bo/', 'shama/my_template_bo'),
+ array('thelia-email-template', 'templates/email/my_template_email/', 'shama/my_template_email'),
+ array('tusk-task', '.tusk/tasks/my_task/', 'shama/my_task'),
+ array('typo3-flow-package', 'Packages/Application/my_package/', 'shama/my_package'),
+ array('typo3-flow-build', 'Build/my_package/', 'shama/my_package'),
+ array('typo3-cms-extension', 'typo3conf/ext/my_extension/', 'shama/my_extension'),
+ array('whmcs-gateway', 'modules/gateways/gateway_name/', 'vendor/gateway_name'),
+ array('wolfcms-plugin', 'wolf/plugins/my_plugin/', 'shama/my_plugin'),
+ array('wordpress-plugin', 'wp-content/plugins/my_plugin/', 'shama/my_plugin'),
+ array('wordpress-muplugin', 'wp-content/mu-plugins/my_plugin/', 'shama/my_plugin'),
+ array('zend-extra', 'extras/library/zend_test/', 'shama/zend_test'),
+ array('zikula-module', 'modules/my-test_module/', 'my/test_module'),
+ array('zikula-theme', 'themes/my-test_theme/', 'my/test_theme'),
+ );
+ }
+
+ /**
+ * testGetCakePHPInstallPathException
+ *
+ * @return void
+ *
+ * @expectedException \InvalidArgumentException
+ */
+ public function testGetCakePHPInstallPathException()
+ {
+ $installer = new Installer($this->io, $this->composer);
+ $package = new Package('shama/ftp', '1.0.0', '1.0.0');
+
+ $package->setType('cakephp-whoops');
+ $result = $installer->getInstallPath($package);
+ }
+
+ /**
+ * testCustomInstallPath
+ */
+ public function testCustomInstallPath()
+ {
+ $installer = new Installer($this->io, $this->composer);
+ $package = new Package('shama/ftp', '1.0.0', '1.0.0');
+ $package->setType('cakephp-plugin');
+ $consumerPackage = new RootPackage('foo/bar', '1.0.0', '1.0.0');
+ $this->composer->setPackage($consumerPackage);
+ $consumerPackage->setExtra(array(
+ 'installer-paths' => array(
+ 'my/custom/path/{$name}/' => array(
+ 'shama/ftp',
+ 'foo/bar',
+ ),
+ ),
+ ));
+ $result = $installer->getInstallPath($package);
+ $this->assertEquals('my/custom/path/Ftp/', $result);
+ }
+
+ /**
+ * testCustomInstallerName
+ */
+ public function testCustomInstallerName()
+ {
+ $installer = new Installer($this->io, $this->composer);
+ $package = new Package('shama/cakephp-ftp-plugin', '1.0.0', '1.0.0');
+ $package->setType('cakephp-plugin');
+ $package->setExtra(array(
+ 'installer-name' => 'FTP',
+ ));
+ $result = $installer->getInstallPath($package);
+ $this->assertEquals('Plugin/FTP/', $result);
+ }
+
+ /**
+ * testCustomTypePath
+ */
+ public function testCustomTypePath()
+ {
+ $installer = new Installer($this->io, $this->composer);
+ $package = new Package('slbmeh/my_plugin', '1.0.0', '1.0.0');
+ $package->setType('wordpress-plugin');
+ $consumerPackage = new RootPackage('foo/bar', '1.0.0', '1.0.0');
+ $this->composer->setPackage($consumerPackage);
+ $consumerPackage->setExtra(array(
+ 'installer-paths' => array(
+ 'my/custom/path/{$name}/' => array(
+ 'type:wordpress-plugin'
+ ),
+ ),
+ ));
+ $result = $installer->getInstallPath($package);
+ $this->assertEquals('my/custom/path/my_plugin/', $result);
+ }
+
+ /**
+ * testNoVendorName
+ */
+ public function testNoVendorName()
+ {
+ $installer = new Installer($this->io, $this->composer);
+ $package = new Package('sfPhpunitPlugin', '1.0.0', '1.0.0');
+
+ $package->setType('symfony1-plugin');
+ $result = $installer->getInstallPath($package);
+ $this->assertEquals('plugins/sfPhpunitPlugin/', $result);
+ }
+
+ /**
+ * testTypo3Inflection
+ */
+ public function testTypo3Inflection()
+ {
+ $installer = new Installer($this->io, $this->composer);
+ $package = new Package('typo3/fluid', '1.0.0', '1.0.0');
+
+ $package->setAutoload(array(
+ 'psr-0' => array(
+ 'TYPO3\\Fluid' => 'Classes',
+ ),
+ ));
+
+ $package->setType('typo3-flow-package');
+ $result = $installer->getInstallPath($package);
+ $this->assertEquals('Packages/Application/TYPO3.Fluid/', $result);
+ }
+
+ public function testUninstallAndDeletePackageFromLocalRepo()
+ {
+ $package = new Package('foo', '1.0.0', '1.0.0');
+
+ $installer = $this->getMock('Composer\Installers\Installer', array('getInstallPath'), array($this->io, $this->composer));
+ $installer->expects($this->once())->method('getInstallPath')->with($package)->will($this->returnValue(sys_get_temp_dir().'/foo'));
+
+ $repo = $this->getMock('Composer\Repository\InstalledRepositoryInterface');
+ $repo->expects($this->once())->method('hasPackage')->with($package)->will($this->returnValue(true));
+ $repo->expects($this->once())->method('removePackage')->with($package);
+
+ $installer->uninstall($repo, $package);
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/MediaWikiInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/MediaWikiInstallerTest.php
new file mode 100644
index 000000000..3675e188b
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/MediaWikiInstallerTest.php
@@ -0,0 +1,66 @@
+installer = new MediaWikiInstaller(
+ new Package('NyanCat', '4.2', '4.2'),
+ new Composer()
+ );
+ }
+
+ /**
+ * @dataProvider packageNameInflectionProvider
+ */
+ public function testInflectPackageVars($type, $name, $expected)
+ {
+ $this->assertEquals(
+ $this->installer->inflectPackageVars(array('name' => $name, 'type'=>$type)),
+ array('name' => $expected, 'type'=>$type)
+ );
+ }
+
+ public function packageNameInflectionProvider()
+ {
+ return array(
+ array(
+ 'mediawiki-extension',
+ 'sub-page-list',
+ 'SubPageList',
+ ),
+ array(
+ 'mediawiki-extension',
+ 'sub-page-list-extension',
+ 'SubPageList',
+ ),
+ array(
+ 'mediawiki-extension',
+ 'semantic-mediawiki',
+ 'SemanticMediawiki',
+ ),
+ // tests that exactly one '-skin' is cut off, and that skins do not get ucwords treatment like extensions
+ array(
+ 'mediawiki-skin',
+ 'some-skin-skin',
+ 'some-skin',
+ ),
+ // tests that names without '-skin' suffix stay valid
+ array(
+ 'mediawiki-skin',
+ 'someotherskin',
+ 'someotherskin',
+ ),
+ );
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/OctoberInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/OctoberInstallerTest.php
new file mode 100644
index 000000000..fd427cdc3
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/OctoberInstallerTest.php
@@ -0,0 +1,66 @@
+installer = new OctoberInstaller(
+ new Package('NyanCat', '4.2', '4.2'),
+ new Composer()
+ );
+ }
+
+ /**
+ * @dataProvider packageNameInflectionProvider
+ */
+ public function testInflectPackageVars($type, $name, $expected)
+ {
+ $this->assertEquals(
+ $this->installer->inflectPackageVars(array('name' => $name, 'type' => $type)),
+ array('name' => $expected, 'type' => $type)
+ );
+ }
+
+ public function packageNameInflectionProvider()
+ {
+ return array(
+ array(
+ 'october-plugin',
+ 'subpagelist',
+ 'subpagelist',
+ ),
+ array(
+ 'october-plugin',
+ 'subpagelist-plugin',
+ 'subpagelist',
+ ),
+ array(
+ 'october-plugin',
+ 'semanticoctober',
+ 'semanticoctober',
+ ),
+ // tests that exactly one '-theme' is cut off
+ array(
+ 'october-theme',
+ 'some-theme-theme',
+ 'some-theme',
+ ),
+ // tests that names without '-theme' suffix stay valid
+ array(
+ 'october-theme',
+ 'someothertheme',
+ 'someothertheme',
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PimcoreInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PimcoreInstallerTest.php
new file mode 100644
index 000000000..ea79374bf
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PimcoreInstallerTest.php
@@ -0,0 +1,44 @@
+package = new Package('CamelCased', '1.0', '1.0');
+ $this->io = $this->getMock('Composer\IO\PackageInterface');
+ $this->composer = new Composer();
+ }
+
+ /**
+ * testInflectPackageVars
+ *
+ * @return void
+ */
+ public function testInflectPackageVars()
+ {
+ $installer = new PimcoreInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'CamelCased'));
+ $this->assertEquals($result, array('name' => 'CamelCased'));
+
+ $installer = new PimcoreInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'with-dash'));
+ $this->assertEquals($result, array('name' => 'WithDash'));
+
+ $installer = new PimcoreInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'with_underscore'));
+ $this->assertEquals($result, array('name' => 'WithUnderscore'));
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PiwikInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PiwikInstallerTest.php
new file mode 100644
index 000000000..8d9ff3f82
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PiwikInstallerTest.php
@@ -0,0 +1,63 @@
+package = new Package('VisitSummary', '1.0', '1.0');
+ $this->io = $this->getMock('Composer\IO\PackageInterface');
+ $this->composer = new Composer();
+ }
+
+ /**
+ * testInflectPackageVars
+ *
+ * @return void
+ */
+ public function testInflectPackageVars()
+ {
+ $installer = new PiwikInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'VisitSummary'));
+ $this->assertEquals($result, array('name' => 'VisitSummary'));
+
+ $installer = new PiwikInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'visit-summary'));
+ $this->assertEquals($result, array('name' => 'VisitSummary'));
+
+ $installer = new PiwikInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'visit_summary'));
+ $this->assertEquals($result, array('name' => 'VisitSummary'));
+ }
+
+}
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/TestCase.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/TestCase.php
new file mode 100644
index 000000000..6418a03b8
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/TestCase.php
@@ -0,0 +1,64 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Installers\Test;
+
+use Composer\Package\Version\VersionParser;
+use Composer\Package\Package;
+use Composer\Package\AliasPackage;
+use Composer\Package\LinkConstraint\VersionConstraint;
+use Composer\Util\Filesystem;
+
+abstract class TestCase extends \PHPUnit_Framework_TestCase
+{
+ private static $parser;
+
+ protected static function getVersionParser()
+ {
+ if (!self::$parser) {
+ self::$parser = new VersionParser();
+ }
+
+ return self::$parser;
+ }
+
+ protected function getVersionConstraint($operator, $version)
+ {
+ return new VersionConstraint(
+ $operator,
+ self::getVersionParser()->normalize($version)
+ );
+ }
+
+ protected function getPackage($name, $version)
+ {
+ $normVersion = self::getVersionParser()->normalize($version);
+
+ return new Package($name, $normVersion, $version);
+ }
+
+ protected function getAliasPackage($package, $version)
+ {
+ $normVersion = self::getVersionParser()->normalize($version);
+
+ return new AliasPackage($package, $normVersion, $version);
+ }
+
+ protected function ensureDirectoryExistsAndClear($directory)
+ {
+ $fs = new Filesystem();
+ if (is_dir($directory)) {
+ $fs->removeDirectory($directory);
+ }
+ mkdir($directory, 0777, true);
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/tests/bootstrap.php b/web/api/app/vendor/composer/installers/tests/bootstrap.php
new file mode 100644
index 000000000..30c8fdc67
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/bootstrap.php
@@ -0,0 +1,4 @@
+add('Composer\Installers\Test', __DIR__);
diff --git a/web/api/app/webroot/.htaccess b/web/api/app/webroot/.htaccess
index 1f19e4c06..f08afa8b2 100644
--- a/web/api/app/webroot/.htaccess
+++ b/web/api/app/webroot/.htaccess
@@ -3,4 +3,5 @@
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
+ RewriteBase /zm/api
diff --git a/web/api/cmake_install.cmake b/web/api/cmake_install.cmake
new file mode 100644
index 000000000..677eb9b9f
--- /dev/null
+++ b/web/api/cmake_install.cmake
@@ -0,0 +1,34 @@
+# Install script for directory: /home/ubuntu/zm/ZoneMinder/web/api
+
+# Set the install prefix
+IF(NOT DEFINED CMAKE_INSTALL_PREFIX)
+ SET(CMAKE_INSTALL_PREFIX "/usr/local")
+ENDIF(NOT DEFINED CMAKE_INSTALL_PREFIX)
+STRING(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
+
+# Set the install configuration name.
+IF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)
+ IF(BUILD_TYPE)
+ STRING(REGEX REPLACE "^[^A-Za-z0-9_]+" ""
+ CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}")
+ ELSE(BUILD_TYPE)
+ SET(CMAKE_INSTALL_CONFIG_NAME "Release")
+ ENDIF(BUILD_TYPE)
+ MESSAGE(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"")
+ENDIF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)
+
+# Set the component getting installed.
+IF(NOT CMAKE_INSTALL_COMPONENT)
+ IF(COMPONENT)
+ MESSAGE(STATUS "Install component: \"${COMPONENT}\"")
+ SET(CMAKE_INSTALL_COMPONENT "${COMPONENT}")
+ ELSE(COMPONENT)
+ SET(CMAKE_INSTALL_COMPONENT)
+ ENDIF(COMPONENT)
+ENDIF(NOT CMAKE_INSTALL_COMPONENT)
+
+# Install shared libraries without execute permission?
+IF(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)
+ SET(CMAKE_INSTALL_SO_NO_EXE "1")
+ENDIF(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)
+
From d915d859eda8862884dfebaa73cd6ce383594555 Mon Sep 17 00:00:00 2001
From: Andrew Bauer
Date: Thu, 11 Jun 2015 11:56:39 -0500
Subject: [PATCH 2/2] Convert Crud to submodule
---
.gitmodules | 3 +
web/api/app/Plugin/Crud | 1 +
web/api/app/Plugin/Crud/.editorconfig | 16 -
web/api/app/Plugin/Crud/.semver | 5 -
web/api/app/Plugin/Crud/.travis.yml | 38 -
web/api/app/Plugin/Crud/CONTRIBUTING.md | 69 -
.../Console/Command/TranslationsShell.php | 310 ----
.../Controller/Component/CrudComponent.php | 845 ----------
.../Controller/Crud/Action/AddCrudAction.php | 131 --
.../Crud/Action/DeleteCrudAction.php | 108 --
.../Controller/Crud/Action/EditCrudAction.php | 293 ----
.../Crud/Action/IndexCrudAction.php | 120 --
.../Controller/Crud/Action/ViewCrudAction.php | 94 --
.../Crud/Controller/Crud/CrudAction.php | 441 -----
.../Crud/Controller/Crud/CrudBaseObject.php | 277 ----
.../Crud/Controller/Crud/CrudListener.php | 67 -
.../Crud/Controller/Crud/CrudSubject.php | 157 --
.../Crud/Listener/ApiFieldFilterListener.php | 348 ----
.../Controller/Crud/Listener/ApiListener.php | 443 -----
.../Crud/Listener/ApiPaginationListener.php | 59 -
.../Crud/Listener/ApiQueryLogListener.php | 100 --
.../Listener/ApiTransformationListener.php | 325 ----
.../Crud/Listener/DebugKitListener.php | 157 --
.../Crud/Listener/RedirectListener.php | 176 --
.../Crud/Listener/RelatedModelsListener.php | 210 ---
.../Crud/Listener/ScaffoldListener.php | 159 --
.../Crud/Listener/SearchListener.php | 211 ---
.../Crud/Error/CrudExceptionRenderer.php | 132 --
.../Exception/CrudValidationException.php | 98 --
web/api/app/Plugin/Crud/LICENSE.txt | 21 -
.../Plugin/Crud/Lib/CrudControllerTrait.php | 64 -
.../app/Plugin/Crud/Lib/Panel/CrudPanel.php | 125 --
web/api/app/Plugin/Crud/README.md | 59 -
.../Crud/Routing/Filter/FakeHeadFilter.php | 60 -
.../Crud/Routing/Filter/HttpMethodFilter.php | 104 --
.../app/Plugin/Crud/Test/Case/AllCrudTest.php | 34 -
.../Console/Command/TranslationsShellTest.php | 449 -----
.../Component/CrudComponentTest.php | 1174 -------------
.../Test/Case/Controller/Component/models.php | 58 -
.../Crud/Action/AddCrudActionTest.php | 306 ----
.../Crud/Action/DeleteCrudActionTest.php | 431 -----
.../Crud/Action/EditCrudActionTest.php | 1104 -------------
.../Crud/Action/IndexCrudActionTest.php | 341 ----
.../Crud/Action/ViewCrudActionTest.php | 365 -----
.../Case/Controller/Crud/CrudActionTest.php | 989 -----------
.../Case/Controller/Crud/CrudListenerTest.php | 19 -
.../Case/Controller/Crud/CrudSubjectTest.php | 83 -
.../Listener/ApiFieldFilterListenerTest.php | 438 -----
.../Crud/Listener/ApiListenerTest.php | 1450 -----------------
.../Listener/ApiPaginationListenerTest.php | 167 --
.../Crud/Listener/ApiQueryLogListenerTest.php | 196 ---
.../ApiTransformationListenerTest.php | 1441 ----------------
.../Crud/Listener/RedirectListenerTest.php | 529 ------
.../Listener/RelatedModelsListenerTest.php | 826 ----------
.../Crud/Listener/ScaffoldListenerTest.php | 323 ----
.../Crud/Listener/SearchListenerTest.php | 719 --------
.../Case/Error/CrudExceptionRendererTest.php | 528 ------
.../Test/Case/Lib/Panel/CrudPanelTest.php | 89 -
.../Routing/Filter/FakeHeadFilterTest.php | 51 -
.../Routing/Filter/HttpMethodFilterTest.php | 207 ---
.../Crud/Test/Fixture/PostsTagFixture.php | 35 -
.../Test/Support/CrudControllerTestCase.php | 169 --
.../Plugin/Crud/Test/Support/CrudTestCase.php | 168 --
web/api/app/Plugin/Crud/View/CrudJsonView.php | 48 -
web/api/app/Plugin/Crud/View/CrudXmlView.php | 52 -
.../Plugin/Crud/View/Elements/crud_panel.ctp | 17 -
web/api/app/Plugin/Crud/composer.json | 57 -
67 files changed, 4 insertions(+), 18685 deletions(-)
create mode 100644 .gitmodules
create mode 160000 web/api/app/Plugin/Crud
delete mode 100644 web/api/app/Plugin/Crud/.editorconfig
delete mode 100644 web/api/app/Plugin/Crud/.semver
delete mode 100644 web/api/app/Plugin/Crud/.travis.yml
delete mode 100644 web/api/app/Plugin/Crud/CONTRIBUTING.md
delete mode 100644 web/api/app/Plugin/Crud/Console/Command/TranslationsShell.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Component/CrudComponent.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Action/AddCrudAction.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Action/DeleteCrudAction.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Action/EditCrudAction.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Action/IndexCrudAction.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Action/ViewCrudAction.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/CrudAction.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/CrudBaseObject.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/CrudListener.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/CrudSubject.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiFieldFilterListener.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiListener.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiPaginationListener.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiQueryLogListener.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiTransformationListener.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/DebugKitListener.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/RedirectListener.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/RelatedModelsListener.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/ScaffoldListener.php
delete mode 100644 web/api/app/Plugin/Crud/Controller/Crud/Listener/SearchListener.php
delete mode 100644 web/api/app/Plugin/Crud/Error/CrudExceptionRenderer.php
delete mode 100644 web/api/app/Plugin/Crud/Error/Exception/CrudValidationException.php
delete mode 100644 web/api/app/Plugin/Crud/LICENSE.txt
delete mode 100644 web/api/app/Plugin/Crud/Lib/CrudControllerTrait.php
delete mode 100644 web/api/app/Plugin/Crud/Lib/Panel/CrudPanel.php
delete mode 100644 web/api/app/Plugin/Crud/README.md
delete mode 100644 web/api/app/Plugin/Crud/Routing/Filter/FakeHeadFilter.php
delete mode 100644 web/api/app/Plugin/Crud/Routing/Filter/HttpMethodFilter.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/AllCrudTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Console/Command/TranslationsShellTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Component/CrudComponentTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Component/models.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/AddCrudActionTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/DeleteCrudActionTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/EditCrudActionTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/IndexCrudActionTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/ViewCrudActionTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudActionTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudListenerTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudSubjectTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiFieldFilterListenerTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiListenerTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiPaginationListenerTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiQueryLogListenerTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiTransformationListenerTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/RedirectListenerTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/RelatedModelsListenerTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ScaffoldListenerTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/SearchListenerTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Error/CrudExceptionRendererTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Lib/Panel/CrudPanelTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Routing/Filter/FakeHeadFilterTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Case/Routing/Filter/HttpMethodFilterTest.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Fixture/PostsTagFixture.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Support/CrudControllerTestCase.php
delete mode 100644 web/api/app/Plugin/Crud/Test/Support/CrudTestCase.php
delete mode 100644 web/api/app/Plugin/Crud/View/CrudJsonView.php
delete mode 100644 web/api/app/Plugin/Crud/View/CrudXmlView.php
delete mode 100644 web/api/app/Plugin/Crud/View/Elements/crud_panel.ctp
delete mode 100644 web/api/app/Plugin/Crud/composer.json
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 000000000..3899c241c
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "web/api/app/Plugin/Crud"]
+ path = web/api/app/Plugin/Crud
+ url = https://github.com/FriendsOfCake/crud.git
diff --git a/web/api/app/Plugin/Crud b/web/api/app/Plugin/Crud
new file mode 160000
index 000000000..f1cf70fff
--- /dev/null
+++ b/web/api/app/Plugin/Crud
@@ -0,0 +1 @@
+Subproject commit f1cf70ffff0657db29388eca94e6c5ea4944a164
diff --git a/web/api/app/Plugin/Crud/.editorconfig b/web/api/app/Plugin/Crud/.editorconfig
deleted file mode 100644
index c5ba6f48c..000000000
--- a/web/api/app/Plugin/Crud/.editorconfig
+++ /dev/null
@@ -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
diff --git a/web/api/app/Plugin/Crud/.semver b/web/api/app/Plugin/Crud/.semver
deleted file mode 100644
index 8a121e67e..000000000
--- a/web/api/app/Plugin/Crud/.semver
+++ /dev/null
@@ -1,5 +0,0 @@
----
-:major: 3
-:minor: 0
-:patch: 10
-:special: ''
diff --git a/web/api/app/Plugin/Crud/.travis.yml b/web/api/app/Plugin/Crud/.travis.yml
deleted file mode 100644
index 7407ecf35..000000000
--- a/web/api/app/Plugin/Crud/.travis.yml
+++ /dev/null
@@ -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
diff --git a/web/api/app/Plugin/Crud/CONTRIBUTING.md b/web/api/app/Plugin/Crud/CONTRIBUTING.md
deleted file mode 100644
index 49fd9a216..000000000
--- a/web/api/app/Plugin/Crud/CONTRIBUTING.md
+++ /dev/null
@@ -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
diff --git a/web/api/app/Plugin/Crud/Console/Command/TranslationsShell.php b/web/api/app/Plugin/Crud/Console/Command/TranslationsShell.php
deleted file mode 100644
index b4b86b5fe..000000000
--- a/web/api/app/Plugin/Crud/Console/Command/TranslationsShell.php
+++ /dev/null
@@ -1,310 +0,0 @@
-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('No controllers found to be processed ');
- 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[] = '_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("Skipping argument: $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("Skipping: $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("Skipping: $className, doesn't use any models", 1, Shell::VERBOSE);
- return;
- }
-
- if (!isset($Controller->Crud)) {
- $this->out("Skipping: $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('Skipping: ' . $text, 1, Shell::VERBOSE);
- } else {
- $this->out('Adding: ' . $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;
- }
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Component/CrudComponent.php b/web/api/app/Plugin/Crud/Controller/Component/CrudComponent.php
deleted file mode 100644
index 5a49fd091..000000000
--- a/web/api/app/Plugin/Crud/Controller/Component/CrudComponent.php
+++ /dev/null
@@ -1,845 +0,0 @@
- $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;
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Action/AddCrudAction.php b/web/api/app/Plugin/Crud/Controller/Crud/Action/AddCrudAction.php
deleted file mode 100644
index 3163dc29e..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/Action/AddCrudAction.php
+++ /dev/null
@@ -1,131 +0,0 @@
- 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();
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Action/DeleteCrudAction.php b/web/api/app/Plugin/Crud/Controller/Crud/Action/DeleteCrudAction.php
deleted file mode 100644
index e5f0184ed..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/Action/DeleteCrudAction.php
+++ /dev/null
@@ -1,108 +0,0 @@
- 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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Action/EditCrudAction.php b/web/api/app/Plugin/Crud/Controller/Crud/Action/EditCrudAction.php
deleted file mode 100644
index 1d5efadfe..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/Action/EditCrudAction.php
+++ /dev/null
@@ -1,293 +0,0 @@
- 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']);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Action/IndexCrudAction.php b/web/api/app/Plugin/Crud/Controller/Crud/Action/IndexCrudAction.php
deleted file mode 100644
index b93cb2b67..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/Action/IndexCrudAction.php
+++ /dev/null
@@ -1,120 +0,0 @@
- 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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Action/ViewCrudAction.php b/web/api/app/Plugin/Crud/Controller/Crud/Action/ViewCrudAction.php
deleted file mode 100644
index 6248fea29..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/Action/ViewCrudAction.php
+++ /dev/null
@@ -1,94 +0,0 @@
- 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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/CrudAction.php b/web/api/app/Plugin/Crud/Controller/Crud/CrudAction.php
deleted file mode 100644
index c35717b9e..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/CrudAction.php
+++ /dev/null
@@ -1,441 +0,0 @@
-_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;
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/CrudBaseObject.php b/web/api/app/Plugin/Crud/Controller/Crud/CrudBaseObject.php
deleted file mode 100644
index d20b1dee8..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/CrudBaseObject.php
+++ /dev/null
@@ -1,277 +0,0 @@
-_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;
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/CrudListener.php b/web/api/app/Plugin/Crud/Controller/Crud/CrudListener.php
deleted file mode 100644
index 3a82b7e9c..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/CrudListener.php
+++ /dev/null
@@ -1,67 +0,0 @@
- '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;
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/CrudSubject.php b/web/api/app/Plugin/Crud/Controller/Crud/CrudSubject.php
deleted file mode 100644
index 1c4309c35..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/CrudSubject.php
+++ /dev/null
@@ -1,157 +0,0 @@
-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');
- }
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiFieldFilterListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiFieldFilterListener.php
deleted file mode 100644
index 2b6bf8e88..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiFieldFilterListener.php
+++ /dev/null
@@ -1,348 +0,0 @@
-_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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiListener.php
deleted file mode 100644
index 5fc25ca2b..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiListener.php
+++ /dev/null
@@ -1,443 +0,0 @@
- 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);
- }
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiPaginationListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiPaginationListener.php
deleted file mode 100644
index 263eaa8d0..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiPaginationListener.php
+++ /dev/null
@@ -1,59 +0,0 @@
- 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);
- }
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiQueryLogListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiQueryLogListener.php
deleted file mode 100644
index 9a575facf..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiQueryLogListener.php
+++ /dev/null
@@ -1,100 +0,0 @@
- 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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiTransformationListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiTransformationListener.php
deleted file mode 100644
index 99605bcb4..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ApiTransformationListener.php
+++ /dev/null
@@ -1,325 +0,0 @@
- 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);
- }
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/DebugKitListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/DebugKitListener.php
deleted file mode 100644
index 778892c4d..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/Listener/DebugKitListener.php
+++ /dev/null
@@ -1,157 +0,0 @@
- 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');
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/RedirectListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/RedirectListener.php
deleted file mode 100644
index c3746f197..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/Listener/RedirectListener.php
+++ /dev/null
@@ -1,176 +0,0 @@
- 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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/RelatedModelsListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/RelatedModelsListener.php
deleted file mode 100644
index 7e5313541..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/Listener/RelatedModelsListener.php
+++ /dev/null
@@ -1,210 +0,0 @@
-_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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ScaffoldListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/ScaffoldListener.php
deleted file mode 100644
index 559bc67b5..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/Listener/ScaffoldListener.php
+++ /dev/null
@@ -1,159 +0,0 @@
- '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;
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Controller/Crud/Listener/SearchListener.php b/web/api/app/Plugin/Crud/Controller/Crud/Listener/SearchListener.php
deleted file mode 100644
index 54e96bfa7..000000000
--- a/web/api/app/Plugin/Crud/Controller/Crud/Listener/SearchListener.php
+++ /dev/null
@@ -1,211 +0,0 @@
- 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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Error/CrudExceptionRenderer.php b/web/api/app/Plugin/Crud/Error/CrudExceptionRenderer.php
deleted file mode 100644
index 8c84655cb..000000000
--- a/web/api/app/Plugin/Crud/Error/CrudExceptionRenderer.php
+++ /dev/null
@@ -1,132 +0,0 @@
-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;
- }
-}
diff --git a/web/api/app/Plugin/Crud/Error/Exception/CrudValidationException.php b/web/api/app/Plugin/Crud/Error/Exception/CrudValidationException.php
deleted file mode 100644
index 368c311b2..000000000
--- a/web/api/app/Plugin/Crud/Error/Exception/CrudValidationException.php
+++ /dev/null
@@ -1,98 +0,0 @@
-_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;
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/LICENSE.txt b/web/api/app/Plugin/Crud/LICENSE.txt
deleted file mode 100644
index bbf75212a..000000000
--- a/web/api/app/Plugin/Crud/LICENSE.txt
+++ /dev/null
@@ -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.
diff --git a/web/api/app/Plugin/Crud/Lib/CrudControllerTrait.php b/web/api/app/Plugin/Crud/Lib/CrudControllerTrait.php
deleted file mode 100644
index 9921e5fc1..000000000
--- a/web/api/app/Plugin/Crud/Lib/CrudControllerTrait.php
+++ /dev/null
@@ -1,64 +0,0 @@
-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;
- }
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Lib/Panel/CrudPanel.php b/web/api/app/Plugin/Crud/Lib/Panel/CrudPanel.php
deleted file mode 100644
index 4821ffaa5..000000000
--- a/web/api/app/Plugin/Crud/Lib/Panel/CrudPanel.php
+++ /dev/null
@@ -1,125 +0,0 @@
-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;
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/README.md b/web/api/app/Plugin/Crud/README.md
deleted file mode 100644
index c99173964..000000000
--- a/web/api/app/Plugin/Crud/README.md
+++ /dev/null
@@ -1,59 +0,0 @@
-[](https://waffle.io/friendsofcake/crud)
-[](https://travis-ci.org/FriendsOfCake/crud)
-[](https://coveralls.io/r/FriendsOfCake/crud?branch=master)
-[](https://packagist.org/packages/FriendsOfCake/crud)
-[](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.
diff --git a/web/api/app/Plugin/Crud/Routing/Filter/FakeHeadFilter.php b/web/api/app/Plugin/Crud/Routing/Filter/FakeHeadFilter.php
deleted file mode 100644
index 25cba5c70..000000000
--- a/web/api/app/Plugin/Crud/Routing/Filter/FakeHeadFilter.php
+++ /dev/null
@@ -1,60 +0,0 @@
-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';
- }
- }
-}
diff --git a/web/api/app/Plugin/Crud/Routing/Filter/HttpMethodFilter.php b/web/api/app/Plugin/Crud/Routing/Filter/HttpMethodFilter.php
deleted file mode 100644
index c7061361c..000000000
--- a/web/api/app/Plugin/Crud/Routing/Filter/HttpMethodFilter.php
+++ /dev/null
@@ -1,104 +0,0 @@
-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;
- }
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/AllCrudTest.php b/web/api/app/Plugin/Crud/Test/Case/AllCrudTest.php
deleted file mode 100644
index 4f09ca87f..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/AllCrudTest.php
+++ /dev/null
@@ -1,34 +0,0 @@
-addTestDirectory($folder);
- }
-
- $suite->addTestDirectory($testPath);
- return $suite;
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Console/Command/TranslationsShellTest.php b/web/api/app/Plugin/Crud/Test/Case/Console/Command/TranslationsShellTest.php
deleted file mode 100644
index ee34ce3f1..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Console/Command/TranslationsShellTest.php
+++ /dev/null
@@ -1,449 +0,0 @@
-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 = <<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 = <<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);
- }
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Component/CrudComponentTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Component/CrudComponentTest.php
deleted file mode 100644
index 01763efff..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Component/CrudComponentTest.php
+++ /dev/null
@@ -1,1174 +0,0 @@
-_log[] = array(
- 'name' => $event->name(),
- 'subject' => $event->subject()
- );
- parent::dispatch($event);
- }
-
- public function getLog($params = array()) {
- $params += array('clear' => true, 'format' => 'names');
-
- $log = $this->_log;
-
- if ($params['format'] === 'names') {
- $return = array();
- foreach ($log as $entry) {
- $return[] = $entry['name'];
- }
- $log = $return;
- }
-
- if ($params['clear']) {
- $this->_log = array();
- }
-
- return $log;
- }
-
-}
-
-class CrudExamplesController extends Controller {
-
- public $uses = array('CrudExample');
-
- public $modelClass = 'CrudExample';
-
- public static $componentsArray = array(
- 'Session',
- 'Crud.Crud' => array(
- 'actions' => array(
- 'index',
- 'add',
- 'edit',
- 'delete',
- 'view'
- )
- )
- );
-
- public $paginate = array(
- 'limit' => 1000
- );
-
-/**
- * Make it possible to dynamically define the components array during tests
- *
- * @param CakeRequest $request
- * @param CakeResponse $response
- * @return void
- */
- public function __construct($request = null, $response = null) {
- $this->components = self::$componentsArray;
-
- return parent::__construct($request, $response);
- }
-
-/**
- * add
- *
- * Used in the testAddActionTranslatedBaseline test
- *
- * @return void
- */
- public function add() {
- return $this->Crud->execute();
- }
-
-/**
- * Test that it should render 'search.ctp'
- *
- * @return void
- */
- public function search() {
- return $this->Crud->execute('index');
- }
-
-/**
- * Test that it should render 'index'
- *
- * @return void
- */
- public function index() {
- return $this->Crud->execute('index');
- }
-
-}
-
-/**
- * TestCrudComponent
- *
- * Expose protected methods so we can test them in isolation
- */
-class TestCrudComponent extends CrudComponent {
-
-/**
- * test visibility wrapper - access protected _modelName property
- */
- public function getModelName() {
- return $this->_modelName;
- }
-
-/**
- * test visibility wrapper - call protected method _setModelProperties
- */
- public function setModelProperties() {
- return parent::_setModelProperties();
- }
-
-/**
- * test visibility wrapper - allow on the fly change of action name
- */
- public function setAction($name) {
- $this->_action = $name;
- }
-
-}
-
-class TestListener extends CrudListener {
-
- public $callCount = 0;
-
- public function setup() {
- $this->callCount += 1;
- }
-
-}
-
-/**
- * CrudComponentTestCase
- */
-class CrudComponentTest extends CrudControllerTestCase {
-
-/**
- * fixtures
- *
- * Use the core posts fixture to have something to work on.
- * What fixture is used is almost irrelevant, was chosen as it is simple
- */
- public $fixtures = array(
- 'core.post', 'core.author', 'core.tag', 'core.comment', 'core.flag_tree',
- 'plugin.crud.posts_tag', 'core.cake_session'
- );
-
-/**
- * setUp
- *
- * Setup the classes the crud component needs to be testable
- */
- public function setUp() {
- require_once ('models.php');
-
- parent::setUp();
-
- CakeEventManager::instance(new TestCrudEventManager());
-
- ConnectionManager::getDataSource('test')->getLog();
-
- $this->model = new CrudExample();
-
- $this->controller = $this->getMock(
- 'CrudExamplesController',
- array('header', 'redirect', 'render', '_stop'),
- array(),
- '',
- false
- );
- $this->controller->name = 'CrudExamples';
-
- $this->request = $this->getMock('CakeRequest', array('is', 'method'));
- $this->request->expects($this->any())->method('is')->will($this->returnValue(true));
-
- $response = new CakeResponse();
- $this->controller->__construct($this->request, $response);
- $this->controller->methods = array();
-
- $this->Collection = new ComponentCollection();
- $this->Collection->init($this->controller);
- $this->controller->Components = $this->Collection;
-
- $settings = array(
- 'actions' => array(
- 'index',
- 'add',
- 'edit',
- 'view',
- 'delete'
- )
- );
-
- $this->Crud = new TestCrudComponent($this->Collection, $settings);
- $this->Crud->initialize($this->controller);
- $this->controller->Crud = $this->Crud;
- }
-
-/**
- * tearDown method
- */
- public function tearDown() {
- unset(
- $this->model,
- $this->request,
- $this->controller,
- $this->Crud,
- $this->Collection
- );
-
- parent::tearDown();
- }
-
-/**
- * Test config normalization
- *
- * @return void
- */
- public function testConfigNormalization() {
- $Collection = $this->getMock('ComponentCollection');
-
- $settings = array(
- 'actions' => array(
- 'index',
- 'admin_index',
- 'add' => 'Crud.Add',
- 'view' => array('viewVar' => 'beers'),
- 'edit' => array('viewVar' => 'beers', 'className' => 'MyPlugin.MyEdit'),
- 'foo' => 'index'
- ),
- 'listeners' => array(
- 'Related' => 'Related',
- 'Mylistener' => 'MyPlugin.Mylistener'
- )
- );
- $Crud = $this->getMock('CrudComponent', array('_loadListeners', 'trigger'), array($Collection, $settings));
- $Crud
- ->expects($this->once())
- ->method('_loadListeners');
- $Crud
- ->expects($this->once())
- ->method('trigger');
- $Crud->initialize($this->controller);
-
- $expected = array(
- 'index' => array('className' => 'Crud.Index'),
- 'admin_index' => array('className' => 'Crud.Index'),
- 'add' => array('className' => 'Crud.Add'),
- 'view' => array('viewVar' => 'beers', 'className' => 'Crud.View'),
- 'edit' => array('viewVar' => 'beers', 'className' => 'MyPlugin.MyEdit'),
- 'foo' => array('className' => 'Crud.Index')
- );
- $this->assertEquals($expected, $Crud->settings['actions']);
-
- $expected = array(
- 'Related' => array('className' => 'Crud.Related'),
- 'Mylistener' => array('className' => 'MyPlugin.Mylistener'),
- 'RelatedModels' => array('className' => 'Crud.RelatedModels'),
- );
- $this->assertEquals($expected, $Crud->settings['listeners']);
- }
-
-/**
- * Test deprecated `executeAction` calls `execute` correctly
- *
- */
- public function testExecuteActionToExecute() {
- $Collection = $this->getMock('ComponentCollection');
- $settings = array('actions' => array('index'));
-
- $Crud = $this->getMock('CrudComponent', array('execute'), array($Collection, $settings));
- $Crud
- ->expects($this->once())
- ->method('execute')
- ->with('index', array('foo' => 'bar'));
-
- $Crud->executeAction('index', array('foo' => 'bar'));
- }
-
-/**
- * testEnable
- *
- */
- public function testEnable() {
- $this->Crud->mapAction('puppies', 'view', false);
- $this->Crud->enable('puppies');
-
- $result = $this->Crud->isActionMapped('puppies');
- $this->assertTrue($result);
- }
-
-/**
- * testDisableAction
- *
- */
- public function testDisableAction() {
- $this->Crud->disable('view');
-
- $result = $this->Crud->isActionMapped('view');
- $this->assertFalse($result);
- }
-
-/**
- * testMapAction
- *
- */
- public function testMapAction() {
- $this->Crud->mapAction('puppies', 'view');
-
- $result = $this->Crud->isActionMapped('puppies');
- $this->assertTrue($result);
-
- $this->Crud->mapAction('kittens', array(
- 'className' => 'Crud.index',
- 'relatedModels' => false
- ));
-
- $result = $this->Crud->isActionMapped('kittens');
- $this->assertTrue($result);
-
- $expected = array(
- 'className' => 'Crud.index',
- 'relatedModels' => false
- );
- $this->assertEquals($expected, $this->Crud->config('actions.kittens'));
- }
-
-/**
- * testView
- *
- */
- public function testView() {
- $this->request
- ->expects($this->once())
- ->method('method')
- ->will($this->returnValue('GET'));
-
- $this->controller
- ->expects($this->once())
- ->method('render');
-
- $this->Crud->view('view', 'cupcakes');
- $this->Crud->execute('view', array(1));
- }
-
-/**
- * testIsActionMappedYes
- *
- */
- public function testIsActionMappedYes() {
- $result = $this->Crud->isActionMapped('index');
- $this->assertTrue($result);
-
- $this->controller->action = 'edit';
- $this->Crud->initialize($this->controller);
- $result = $this->Crud->isActionMapped();
- $this->assertTrue($result);
- }
-
-/**
- * testIsActionMappedNo
- *
- */
- public function testIsActionMappedNo() {
- $result = $this->Crud->isActionMapped('puppies');
- $this->assertFalse($result);
-
- $this->controller->action = 'rainbows';
- $this->Crud->initialize($this->controller);
- $result = $this->Crud->isActionMapped();
- $this->assertFalse($result);
- }
-
-/**
- * Tests on method registers an event
- *
- */
- public function testOn() {
- $this->Crud->on('event', 'fakeCallback');
-
- $return = $this->controller->getEventManager()->listeners('Crud.event');
-
- $expected = array(
- array(
- 'callable' => 'fakeCallback',
- 'passParams' => false
- )
- );
- $this->assertSame($expected, $return);
- }
-
-/**
- * tests on method registers an event with extra params
- *
- */
- public function testOnWithPriPriorityy() {
- $this->Crud->on('event', 'fakeCallback');
- $this->Crud->on('event', 'fakeHighPriority', array('priority' => 1));
- $this->Crud->on('event', 'fakeLowPriority', array('priority' => 99999));
-
- $return = $this->controller->getEventManager()->listeners('Crud.event');
-
- $expected = array(
- array(
- 'callable' => 'fakeHighPriority',
- 'passParams' => false
- ),
- array(
- 'callable' => 'fakeCallback',
- 'passParams' => false
- ),
- array(
- 'callable' => 'fakeLowPriority',
- 'passParams' => false
- )
- );
- $this->assertSame($expected, $return);
- }
-
-/**
- * Test if crud complains about unmapped actions
- *
- * @expectedException CakeException
- * @return void
- */
- public function testCrudWillComplainAboutUnmappedAction() {
- $this->Crud->execute('show_all');
- }
-
-/**
- * Test if view with array yields the expected result
- *
- * @return void
- */
- public function testViewWithArrayNewAction() {
- $this->request
- ->expects($this->once())
- ->method('method')
- ->will($this->returnValue('GET'));
-
- $this->request
- ->expects($this->once())
- ->method('method')
- ->will($this->returnValue('GET'));
-
- $this->controller
- ->expects($this->once())
- ->method('render')
- ->with('index');
-
- $this->Crud->mapAction('show_all', 'index');
- $this->Crud->view(array('show_all' => 'index', 'index' => 'overview'));
-
- $this->Crud->execute('show_all');
- }
-
-/**
- * Test if view with array yields the expected result
- *
- * @return void
- */
- public function testViewWithArrayIndexAction() {
- $this->request
- ->expects($this->once())
- ->method('method')
- ->will($this->returnValue('GET'));
-
- $this->controller
- ->expects($this->once())
- ->method('render')
- ->with('overview');
-
- $this->Crud->mapAction('show_all', 'index');
- $this->Crud->view(array('show_all' => 'index', 'index' => 'overview'));
-
- $this->Crud->execute('index');
- }
-
-/**
- * Test that having no mapped model for an action,
- * just use the modelClass from the controller
- *
- * @return void
- */
- public function testSetModelPropertiesDefault() {
- $this->Crud->setAction('index');
- $this->Crud->setModelProperties();
- $this->assertSame('CrudExample', $this->Crud->getModelName());
- }
-
-/**
- * Test that the build in action names can't be used
- * within other plugins
- *
- * @expectedException CakeException
- * @expectedExceptionMessage The build-in CrudActions (Index, View, Add, Edit and Delete) must be loaded from the Crud plugin
- * @return void
- */
- public function testBuildInCrudActionsCantBeUsedInOtherPluginsIndex() {
- $this->Crud->mapAction('test', 'Sample.Index');
- }
-
-/**
- * Test that the build in action names can't be used
- * within other plugins
- *
- * @expectedException CakeException
- * @expectedExceptionMessage The build-in CrudActions (Index, View, Add, Edit and Delete) must be loaded from the Crud plugin
- * @return void
- */
- public function testBuildInCrudActionsCantBeUsedInOtherPluginsView() {
- $this->Crud->mapAction('test', 'Sample.View');
- }
-
-/**
- * Test that the build in action names can't be used
- * within other plugins
- *
- * @expectedException CakeException
- * @expectedExceptionMessage The build-in CrudActions (Index, View, Add, Edit and Delete) must be loaded from the Crud plugin
- * @return void
- */
- public function testBuildInCrudActionsCantBeUsedInOtherPluginsAdd() {
- $this->Crud->mapAction('test', 'Sample.Add');
- }
-
-/**
- * Test that the build in action names can't be used
- * within other plugins
- *
- * @expectedException CakeException
- * @expectedExceptionMessage The build-in CrudActions (Index, View, Add, Edit and Delete) must be loaded from the Crud plugin
- * @return void
- */
- public function testBuildInCrudActionsCantBeUsedInOtherPluginsEdit() {
- $this->Crud->mapAction('test', 'Sample.Edit');
- }
-
-/**
- * Test that the build in action names can't be used
- * within other plugins
- *
- * @expectedException CakeException
- * @expectedExceptionMessage The build-in CrudActions (Index, View, Add, Edit and Delete) must be loaded from the Crud plugin
- * @return void
- */
- public function testBuildInCrudActionsCantBeUsedInOtherPluginsDelete() {
- $this->Crud->mapAction('test', 'Sample.Delete');
- }
-
-/**
- * Test that Providing a CrudAction name that isn't in the
- * list of build-in once, will allow you to use it inside
- * another plugin.
- *
- * It's expected that the plugin CrudSample doesn't exist,
- * the App::uses() where the warning is raised is *after*
- * the check for the above build-in class names
- *
- * @expectedException CakeException
- * @expectedExceptionMessage Plugin CrudSample could not be found.
- * @return void
- */
- public function testCustomCrudActionsCanBeUsedInPlugins() {
- $this->Crud->mapAction('test', 'CrudSample.MyDelete');
- }
-
-/**
- * Test that having a 'search' action in the controller
- * and calling ->execute('index') will still
- * render the 'search' view
- *
- * @return void
- */
- public function testViewCanBeChangedInControllerAction() {
- $this->request
- ->expects($this->once())
- ->method('method')
- ->will($this->returnValue('GET'));
-
- $this->request->action = 'search';
-
- $this->controller
- ->expects($this->once())
- ->method('render')
- ->with('search');
-
- $this->controller->search();
- }
-
-/**
- * Test the default configuration for CrudComponent
- *
- * @return void
- */
- public function testDefaultConfig() {
- $Collection = $this->getMock('ComponentCollection');
-
- $Crud = new CrudComponent($Collection);
-
- $result = $Crud->config();
- $expected = 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
- );
- $this->assertEquals($expected, $result);
- }
-
-/**
- * Test that providing configuration for a new
- * listener in the Crud setting should preserve
- * the defaults and add the new listener to the array
- *
- * @return void
- */
- public function testConstructMerging() {
- $Collection = $this->getMock('ComponentCollection');
-
- $config = array(
- 'listeners' => array(
- 'Api' => 'Crud.Api'
- )
- );
-
- $Crud = new CrudComponent($Collection, $config);
- $result = $Crud->config();
- $expected = array(
- 'actions' => array(),
- 'eventPrefix' => 'Crud',
- 'listeners' => array(
- 'RelatedModels' => 'Crud.RelatedModels',
- 'Api' => 'Crud.Api'
- ),
- '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
- );
- $this->assertEquals($expected, $result);
- }
-
-/**
- * Test that providing configuration for a new
- * listener in the Crud setting should preserve
- * the defaults and add the new listener to the array
- *
- * @return void
- */
- public function testConstructMerging2() {
- $Collection = $this->getMock('ComponentCollection');
-
- $config = array(
- 'listeners' => array(
- )
- );
-
- $Crud = new CrudComponent($Collection, $config);
- $result = $Crud->config();
- $expected = 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
- );
- $this->assertEquals($expected, $result);
- }
-
-/**
- * Test that addListener works - without listener
- * default config
- *
- * @return void
- */
- public function testAddListenerWithoutDefaults() {
- $listeners = $this->Crud->config('listeners');
- $expected = array(
- 'RelatedModels' => array('className' => 'Crud.RelatedModels')
- );
-
- $this->assertEquals($expected, $listeners);
-
- $this->Crud->addListener('Api', 'Crud.Api');
-
- $listeners = $this->Crud->config('listeners');
- $expected = array(
- 'RelatedModels' => array('className' => 'Crud.RelatedModels'),
- 'Api' => array('className' => 'Crud.Api')
- );
- $this->assertEquals($expected, $listeners);
-
- $this->assertEquals(array('className' => 'Crud.Api'), $this->Crud->defaults('listeners', 'Api'));
- }
-
-/**
- * Test that addListener works - with listener
- * default config
- *
- * @return void
- */
- public function testAddListenerWithDefaults() {
- $listeners = $this->Crud->config('listeners');
- $expected = array(
- 'RelatedModels' => array('className' => 'Crud.RelatedModels')
- );
-
- $this->assertEquals($expected, $listeners);
-
- $this->Crud->addListener('Api', 'Crud.Api', array('test' => 1));
-
- $listeners = $this->Crud->config('listeners');
- $expected = array(
- 'RelatedModels' => array('className' => 'Crud.RelatedModels'),
- 'Api' => array('className' => 'Crud.Api', 'test' => 1)
- );
- $this->assertEquals($expected, $listeners);
-
- $this->assertEquals(
- array('className' => 'Crud.Api', 'test' => 1),
- $this->Crud->defaults('listeners', 'Api')
- );
- }
-
-/**
- * Test that removeListener works
- *
- * @return void
- */
- public function testRemoveListener() {
- $listeners = $this->Crud->config('listeners');
- $expected = array(
- 'RelatedModels' => array('className' => 'Crud.RelatedModels')
- );
-
- $this->assertEquals($expected, $listeners);
-
- $this->Crud->removeListener('RelatedModels');
-
- $listeners = $this->Crud->config('listeners');
- $expected = array();
- $this->assertEquals($expected, $listeners);
-
- // Should now throw an exception
- $this->setExpectedException('CakeException', 'Listener "relatedModels" is not configured');
- $this->Crud->listener('relatedModels');
- }
-
-/**
- * Test removing a listener that doesn't exist
- * should return false
- *
- * @return void
- */
- public function testRemoveListenerNoExist() {
- $expected = false;
- $result = $this->Crud->removeListener('invalid_name');
- $this->assertEquals($expected, $result);
- }
-
-/**
- * Test that removeLister works
- *
- * Also ensure that the listener is detached from EventManager
- *
- * @return void
- */
- public function testRemoveListenerAttached() {
- $listeners = $this->Crud->config('listeners');
- $expected = array(
- 'RelatedModels' => array('className' => 'Crud.RelatedModels')
- );
- $this->assertEquals($expected, $listeners);
-
- // Make sure the listener is attached
- $this->Crud->listener('RelatedModels');
-
- // Remove it (including detach)
- $this->Crud->removeListener('RelatedModels');
-
- $listeners = $this->Crud->config('listeners');
- $expected = array();
- $this->assertEquals($expected, $listeners);
-
- // Should now throw an exception
- $this->setExpectedException('CakeException', 'Listener "RelatedModels" is not configured');
- $this->Crud->listener('RelatedModels');
- }
-
-/**
- * Test changing view var for one action works
- *
- * @return void
- */
- public function testViewVarSingleAction() {
- $this->Crud->viewVar('index', 'my_var');
-
- $expected = 'my_var';
- $result = $this->Crud->action('index')->viewVar();
- $this->assertEquals($expected, $result);
- }
-
-/**
- * Test changing view var for multiple actions works
- *
- * @return void
- */
- public function testViewVarMultipleActions() {
- $this->Crud->viewVar(array('index' => 'my_var', 'view' => 'view_var'));
-
- $expected = 'my_var';
- $result = $this->Crud->action('index')->viewVar();
- $this->assertEquals($expected, $result);
-
- $expected = 'view_var';
- $result = $this->Crud->action('view')->viewVar();
- $this->assertEquals($expected, $result);
- }
-
-/**
- * Test changing view var for multiple actions works
- *
- * @return void
- */
- public function testFindMethodMultipleActions() {
- $this->Crud->findMethod(array('index' => 'my_all', 'view' => 'my_view'));
-
- $expected = 'my_all';
- $result = $this->Crud->action('index')->findMethod();
- $this->assertEquals($expected, $result);
-
- $expected = 'my_view';
- $result = $this->Crud->action('view')->findMethod();
- $this->assertEquals($expected, $result);
- }
-
-/**
- * Test setting defaults for one action works
- *
- * @return void
- */
- public function testDefaultsOnAction() {
- $this->Crud->defaults('actions', 'index', array('unit_test' => true));
- $config = $this->Crud->defaults('actions', 'index');
-
- $this->assertTrue($config['unit_test']);
- }
-
-/**
- * Test setting defaults for multiple actions work
- *
- * @return void
- */
- public function testDefaultsMultipleActions() {
- $this->Crud->defaults('actions', array('index', 'view'), array('unit_test' => true));
-
- $config = $this->Crud->defaults('actions', 'index');
- $this->assertTrue($config['unit_test']);
-
- $config = $this->Crud->defaults('actions', 'view');
- $this->assertTrue($config['unit_test']);
- }
-
-/**
- * Test setting defaults for one listener works
- *
- * @return void
- */
- public function testDefaultsOneListener() {
- $this->Crud->defaults('listeners', 'translations', array('unit_test' => true));
- $config = $this->Crud->defaults('listeners', 'translations');
-
- $this->assertTrue($config['unit_test']);
- }
-
-/**
- * Test setting defaults for multiple actions work
- *
- * @return void
- */
- public function testDefaultsMultipleListeners() {
- $this->Crud->defaults('listeners', array('translations', 'relatedModels'), array('unit_test' => true));
-
- $config = $this->Crud->defaults('listeners', 'translations');
- $this->assertTrue($config['unit_test']);
-
- $config = $this->Crud->defaults('listeners', 'relatedModels');
- $this->assertTrue($config['unit_test']);
- }
-
-/**
- * Test setting defaults for one listener works
- *
- * This proves that not setting 'className' doesn't break
- *
- * @return void
- */
- public function testDefaultsListenerNotAlreadyLoaded() {
- $this->Crud->defaults('listeners', 'api', array('unit_test' => true));
- $config = $this->Crud->defaults('listeners', 'api');
- $this->assertTrue($config['unit_test']);
- }
-
-/**
- * Test adding a listener only by a name and no class works
- *
- * By only providing a name, it should default to Crud plugin
- *
- * @return void
- */
- public function testAddListenerOnlyNameNoClassName() {
- $this->Crud->addListener('api');
- $config = $this->Crud->config('listeners');
- $this->assertEquals(array('className' => 'Crud.Api'), $config['api']);
- }
-
-/**
- * Test adding a listener only by a name and a class works
- *
- * By providing a class, it should not default to Crud plugin
- * even though it doesn't contain any plugin.
- *
- * This allow developers to put listeners in app/Controller/Crud
- *
- * @return void
- */
- public function testAddListenerOnlyNameClassName() {
- $this->Crud->addListener('api', 'Api');
- $config = $this->Crud->config('listeners');
- $this->assertEquals(array('className' => 'Api'), $config['api']);
- }
-
-/**
- * Test adding a listener only by its name, with plugin dot syntax
- * works
- *
- * @return void
- */
- public function testAddListenerOnlyNameWithPlugin() {
- $this->Crud->addListener('MyPlugin.Api');
- $config = $this->Crud->config('listeners');
- $this->assertEquals(array('className' => 'MyPlugin.Api'), $config['api']);
- }
-
-/**
- * Test adding a listener only by its name, with plugin dot syntax
- * works
- *
- * @return void
- */
- public function testAddListenerOnlyNameWithPluginLowercase() {
- $this->Crud->addListener('MyPlugin.api');
- $config = $this->Crud->config('listeners');
- $this->assertEquals(array('className' => 'MyPlugin.Api'), $config['api']);
- }
-
-/**
- * Test the Crud sets model and modelClass to NULL
- * if there is no model defined in the controller
- *
- * @return void
- */
- public function testControllerWithEmptyUses() {
- $controller = new Controller(new CakeRequest());
- $this->Crud = new CrudComponent($this->Collection, array('actions' => array('index')));
- $this->Crud->initialize($controller);
- $this->controller->Crud = $this->Crud;
- $this->Crud->action('index');
- $subject = $this->Crud->trigger('sample');
-
- $this->assertNull($subject->model);
- $this->assertNull($subject->modelClass);
- }
-
-/**
- * Test that it's possible to change just one sub key
- * by providing all the parents, without loosing any
- * default settings
- *
- * @return void
- */
- public function testConfigMergeWorks() {
- $this->Crud->config(array('messages' => array('invalidId' => array('code' => 500))));
-
- $expected = array(
- 'code' => 500,
- 'class' => 'BadRequestException',
- 'text' => 'Invalid id'
- );
- $result = $this->Crud->config('messages.invalidId');
- $this->assertEquals($expected, $result);
- }
-
-/**
- * Using $key and value, and specifying no merge should overwrite the value keys
- *
- * @return void
- */
- public function testConfigOverwrite() {
- $this->Crud->config('messages', array('invalidId' => array('code' => 500)), null, false);
-
- $expected = array(
- 'domain' => 'crud',
- 'invalidId' => array(
- 'code' => 500
- ),
- 'recordNotFound' => array(
- 'code' => 404,
- 'class' => 'NotFoundException',
- 'text' => 'Not found'
- ),
- 'badRequestMethod' => array(
- 'code' => 405,
- 'class' => 'MethodNotAllowedException',
- 'text' => 'Method not allowed. This action permits only {methods}'
- )
- );
- $result = $this->Crud->config('messages');
- $this->assertEquals($expected, $result);
- }
-/**
- * Passing an array, and specifying no merge should overwrite the value keys
- *
- * @return void
- */
- public function testConfigOverwriteArray() {
- $this->Crud->config(array('messages' => array('invalidId' => array('code' => 500))), null, false);
-
- $expected = array(
- 'domain' => 'crud',
- 'invalidId' => array(
- 'code' => 500,
- '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}'
- )
- );
- $result = $this->Crud->config('messages');
- $this->assertEquals($expected, $result);
- }
-
-/**
- * Tests that is possible to set the model class to use for the action
- *
- * @return void
- */
- public function testUseModel() {
- $controller = new Controller(new CakeRequest());
- $this->Crud = new CrudComponent($this->Collection, array('actions' => array('index')));
- $this->Crud->initialize($controller);
- $this->controller->Crud = $this->Crud;
- $class = $this->getMockClass('Model');
- $this->Crud->useModel($class);
- $this->Crud->action('index');
- $subject = $this->Crud->trigger('sample');
-
- $this->assertInstanceOf($class, $subject->model);
- $this->assertEquals($class, $subject->modelClass);
- }
-
-/**
- * test_loadListener
- *
- * @return void
- */
- public function test_loadListener() {
- $this->Crud->config('listeners.HasSetup', array(
- 'className' => 'Test'
- ));
-
- $this->setReflectionClassInstance($this->Crud);
- $listener = $this->callProtectedMethod('_loadListener', array('HasSetup'), $this->Crud);
- $this->assertSame(1, $listener->callCount, 'Setup should be called');
- }
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Component/models.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Component/models.php
deleted file mode 100644
index c1fefb8d4..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Component/models.php
+++ /dev/null
@@ -1,58 +0,0 @@
- 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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/AddCrudActionTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/AddCrudActionTest.php
deleted file mode 100644
index c460dde64..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/AddCrudActionTest.php
+++ /dev/null
@@ -1,306 +0,0 @@
-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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/DeleteCrudActionTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/DeleteCrudActionTest.php
deleted file mode 100644
index e869bd446..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/DeleteCrudActionTest.php
+++ /dev/null
@@ -1,431 +0,0 @@
-_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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/EditCrudActionTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/EditCrudActionTest.php
deleted file mode 100644
index c11569065..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/EditCrudActionTest.php
+++ /dev/null
@@ -1,1104 +0,0 @@
- array('Model.id' => 1));
- $data = array('Model' => array('id' => 1));
-
- $Request = $this->getMock('CakeRequest');
-
- $Model = $this
- ->getMock('Model', array('create', 'find', 'escapeField'));
- $Model
- ->expects($this->once())
- ->method('escapeField')
- ->with()
- ->will($this->returnValue('Model.id'));
- $Model
- ->expects($this->once())
- ->method('find')
- ->with('first', $query)
- ->will($this->returnValue($data));
-
- $i = 0;
- $Action = $this
- ->getMockBuilder('EditCrudAction')
- ->disableOriginalConstructor()
- ->setMethods(array('_validateId', '_request', '_model', '_trigger', '_getFindMethod'))
- ->getMock();
- $Action
- ->expects($this->at($i++))
- ->method('_validateId')
- ->with(1)
- ->will($this->returnValue(true));
- $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('_model')
- ->will($this->returnValue($Model));
- $Action
- ->expects($this->at($i++))
- ->method('_getFindMethod')
- ->will($this->returnValue('first'));
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('beforeFind', array('findMethod' => 'first', 'query' => $query))
- ->will($this->returnValue(new CrudSubject(array('query' => $query, 'findMethod' => 'first'))));
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('afterFind', array('id' => 1, 'item' => $data))
- ->will($this->returnValue(new CrudSubject(array('item' => $data))));
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('beforeRender');
-
- $this->setReflectionClassInstance($Action);
- $this->callProtectedMethod('_get', array(1), $Action);
- }
-
-/**
- * Test that calling HTTP PUT on an edit action
- * will trigger the appropriate events and try to
- * update a record in the database
- *
- * This test assumes the best possible case
- * The id provided, it's correct and it's in the db
- *
- * @return void
- */
- public function testActionPut() {
- $Action = $this->_actionSuccess();
- $this->setReflectionClassInstance($Action);
- $this->callProtectedMethod('_put', array(1), $Action);
- }
-
-/**
- * Test that calling HTTP POST on an edit action
- * will trigger the appropriate events and try to
- * update a record in the database
- *
- * This test assumes the best possible case
- * The id provided, it's correct and it's in the db
- *
- * @return void
- */
- public function testActionPost() {
- $Action = $this->_actionSuccess();
- $this->setReflectionClassInstance($Action);
- $this->callProtectedMethod('_post', array(1), $Action);
- }
-
- protected function _actionSuccess() {
- $data = array('Model' => array('id' => 1));
-
- $CrudSubject = new CrudSubject();
-
- $Request = $this->getMock('CakeRequest');
- $Request->data = $data;
-
- $Model = $this
- ->getMockBuilder('Model')
- ->setMethods(array('saveAssociated', 'find', 'escapeField'))
- ->setConstructorArgs(array(array('name' => 'Model')))
- ->getMock();
-
- $i = 0;
- $Action = $this
- ->getMockBuilder('EditCrudAction')
- ->disableOriginalConstructor()
- ->setMethods(array(
- '_validateId', '_request', '_model', '_trigger',
- '_redirect', 'setFlash', 'saveOptions'
- ))
- ->getMock();
- $Action
- ->expects($this->at($i++))
- ->method('_validateId')
- ->with(1)
- ->will($this->returnValue(true));
- $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('_model')
- ->will($this->returnValue($Model));
- $Model
- ->expects($this->once())
- ->method('escapeField')
- ->with()
- ->will($this->returnValue('Model.id'));
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('beforeFind', array('query' => array('conditions' => array('Model.id' => 1)), 'findMethod' => 'count'))
- ->will($this->returnValue(new CrudSubject(array('query' => array('conditions' => array('Model.id' => 1)), 'findMethod' => 'count'))));
- $Model
- ->expects($this->once())
- ->method('find')
- ->with('count', array('conditions' => array('Model.id' => 1)))
- ->will($this->returnValue(true));
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('beforeSave', array('id' => 1));
- $Action
- ->expects($this->at($i++))
- ->method('saveOptions')
- ->will($this->returnValue(array('atomic' => true)));
- $Model
- ->expects($this->once())
- ->method('saveAssociated')
- ->with($data)
- ->will($this->returnValue(true));
- $Action
- ->expects($this->at($i++))
- ->method('setFlash')
- ->with('success');
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('afterSave', array('success' => true, 'created' => false, 'id' => 1))
- ->will($this->returnValue($CrudSubject));
- $Action
- ->expects($this->at($i++))
- ->method('_redirect')
- ->with($CrudSubject, array('action' => 'index'));
- $Action
- ->expects($this->exactly(3))
- ->method('_trigger');
- return $Action;
- }
-
-/**
- * Test that calling HTTP PUT on an edit action
- * will trigger the appropriate events and try to
- * update a record in the database
- *
- * This test assumes the saveAssociated() call fails
- * The id provided, it's correct and it's in the db
- *
- * @return void
- */
- public function testActionPutSaveError() {
- $data = array('Model' => array('id' => 1));
-
- $CrudSubject = new CrudSubject();
-
- $Request = $this->getMock('CakeRequest');
- $Request->data = $data;
-
- $Model = $this
- ->getMockBuilder('Model')
- ->setMethods(array('saveAssociated'))
- ->setConstructorArgs(array(array('name' => 'Model')))
- ->getMock();
-
- $i = 0;
- $Action = $this
- ->getMockBuilder('EditCrudAction')
- ->disableOriginalConstructor()
- ->setMethods(array(
- '_validateId', '_request', '_model', '_trigger',
- '_redirect', 'setFlash', 'saveOptions', '_findRecord'
- ))
- ->getMock();
- $Action
- ->expects($this->at($i++))
- ->method('_validateId')
- ->with(1)
- ->will($this->returnValue(true));
- $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('_findRecord')
- ->with(1, 'count')
- ->will($this->returnValue(true));
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('beforeSave', array('id' => 1));
- $Action
- ->expects($this->at($i++))
- ->method('saveOptions')
- ->will($this->returnValue(array('atomic' => true)));
- $Model
- ->expects($this->once())
- ->method('saveAssociated')
- ->with($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, 'id' => 1))
- ->will($this->returnValue($CrudSubject));
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('beforeRender');
- $Action
- ->expects($this->never())
- ->method('_redirect');
- $Action
- ->expects($this->exactly(3))
- ->method('_trigger');
-
- $this->setReflectionClassInstance($Action);
- $this->callProtectedMethod('_put', array(1), $Action);
- }
-
-/**
- * Test that calling HTTP GET on an edit action
- * will trigger the appropriate events
- *
- * Given an ID, we test what happens if the ID doesn't
- * exist in the database
- *
- * @expectedException NotFoundException
- * @expectedExceptionMessage Not Found
- * @expectedExceptionCode 404
- * @return void
- */
- public function testActionGetWithNonexistingId() {
- $CrudSubject = new CrudSubject();
-
- $query = array('conditions' => array('Model.id' => 1));
-
- $Request = $this->getMock('CakeRequest');
-
- $Model = $this
- ->getMock('Model', array('escapeField', 'find'));
-
- $i = 0;
- $Action = $this
- ->getMockBuilder('EditCrudAction')
- ->disableOriginalConstructor()
- ->setMethods(array(
- '_validateId', '_request', '_model', '_trigger',
- '_redirect', 'setFlash', 'saveOptions', 'message'
- ))
- ->getMock();
- $Action
- ->expects($this->at($i++))
- ->method('_validateId')
- ->with(1)
- ->will($this->returnValue(true));
- $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('_model')
- ->will($this->returnValue($Model));
- $Model
- ->expects($this->once())
- ->method('escapeField')
- ->with()
- ->will($this->returnValue('Model.id'));
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('beforeFind', array('findMethod' => 'first', 'query' => $query))
- ->will($this->returnValue(new CrudSubject(array('query' => $query, 'findMethod' => 'first'))));
- $Model
- ->expects($this->once())
- ->method('find')
- ->with('first', $query)
- ->will($this->returnValue(array()));
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('recordNotFound', array('id' => 1))
- ->will($this->returnValue($CrudSubject));
- $Action
- ->expects($this->at($i++))
- ->method('message')
- ->with('recordNotFound', array('id' => 1))
- ->will($this->returnValue(array('class' => 'NotFoundException', 'text' => 'Not Found', 'code' => 404)));
- $Action
- ->expects($this->exactly(2))
- ->method('_trigger');
-
- $this->setReflectionClassInstance($Action);
- $this->callProtectedMethod('_get', array(1), $Action);
- }
-
-/**
- * Test that calling HTTP GET on an edit action
- * will trigger the appropriate events
- *
- * Given an ID, we test what happens if the ID is invalid
- *
- * @return void
- */
- public function testActionGetWithInvalidId() {
- $i = 0;
- $Action = $this
- ->getMockBuilder('EditCrudAction')
- ->disableOriginalConstructor()
- ->setMethods(array(
- '_validateId'
- ))
- ->getMock();
- $Action
- ->expects($this->at($i++))
- ->method('_validateId')
- ->with(null)
- ->will($this->returnValue(false));
-
- $this->setReflectionClassInstance($Action);
- $this->callProtectedMethod('_get', array(null), $Action);
- }
-
-/**
- * Test that calling HTTP PUT on an edit action
- * will trigger the appropriate events
- *
- * Given an ID, we test what happens if the ID is invalid
- *
- * @return void
- */
- public function testActionPutWithInvalidId() {
- $i = 0;
- $Action = $this
- ->getMockBuilder('EditCrudAction')
- ->disableOriginalConstructor()
- ->setMethods(array(
- '_validateId'
- ))
- ->getMock();
- $Action
- ->expects($this->at($i++))
- ->method('_validateId')
- ->with(null)
- ->will($this->returnValue(false));
-
- $this->setReflectionClassInstance($Action);
- $this->callProtectedMethod('_put', array(null), $Action);
- }
-
-/**
- * Test that calling HTTP GET on an edit 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
- * Additionally the `_getFindMethod` method returns
- * something not-default
- *
- * @return void
- */
- public function testGetWithCustomFindMethod() {
- $query = array('conditions' => array('Model.id' => 1));
- $data = array('Model' => array('id' => 1));
-
- $Request = $this->getMock('CakeRequest');
-
- $Model = $this
- ->getMock('Model', array('create', 'find', 'escapeField'));
-
- $i = 0;
- $Action = $this
- ->getMockBuilder('EditCrudAction')
- ->disableOriginalConstructor()
- ->setMethods(array('_validateId', '_request', '_model', '_trigger', '_getFindMethod'))
- ->getMock();
- $Action
- ->expects($this->at($i++))
- ->method('_validateId')
- ->with(1)
- ->will($this->returnValue(true));
- $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('_model')
- ->will($this->returnValue($Model));
- $Model
- ->expects($this->once())
- ->method('escapeField')
- ->with()
- ->will($this->returnValue('Model.id'));
- $Action
- ->expects($this->at($i++))
- ->method('_getFindMethod')
- ->will($this->returnValue('first'));
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('beforeFind', array('findMethod' => 'first', 'query' => $query))
- ->will($this->returnValue(new CrudSubject(array('query' => $query, 'findMethod' => 'myCustomQuery'))));
- $Model
- ->expects($this->once())
- ->method('find')
- ->with('myCustomQuery', $query)
- ->will($this->returnValue($data));
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('afterFind', array('id' => 1, 'item' => $data))
- ->will($this->returnValue(new CrudSubject(array('item' => $data))));
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('beforeRender');
-
- $this->setReflectionClassInstance($Action);
- $this->callProtectedMethod('_get', array(1), $Action);
- }
-
-/**
- * test_findRecordDefault
- *
- * @return void
- */
- public function test_findRecordDefault() {
- $query = array('conditions' => array('Model.id' => 1));
- $findParams = array('findMethod' => 'special', 'query' => $query);
-
- $i = 0;
- $Model = $this->getMock('Model', array('escapeField', 'find'));
- $Model
- ->expects($this->at($i++))
- ->method('escapeField')
- ->will($this->returnValue('Model.id'));
- $Model
- ->expects($this->at($i++))
- ->method('find')
- ->with('special', $query);
-
- $i = 0;
- $Action = $this
- ->getMockBuilder('EditCrudAction')
- ->disableOriginalConstructor()
- ->setMethods(array('_model', '_getFindMethod', '_trigger'))
- ->getMock();
- $Action
- ->expects($this->at($i++))
- ->method('_model')
- ->will($this->returnValue($Model));
- $Action
- ->expects($this->at($i++))
- ->method('_getFindMethod')
- ->will($this->returnValue('special'));
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('beforeFind', $findParams)
- ->will($this->returnValue(new CrudSubject($findParams)));
-
- $this->setReflectionClassInstance($Action);
- $this->callProtectedMethod('_findRecord', array(1), $Action);
- }
-
-/**
- * test_findRecordOverride
- *
- * @return void
- */
- public function test_findRecordOverride() {
- $query = array('conditions' => array('Model.id' => 1));
- $findParams = array('findMethod' => 'count', 'query' => $query);
-
- $i = 0;
- $Model = $this->getMock('Model', array('escapeField', 'find'));
- $Model
- ->expects($this->at($i++))
- ->method('escapeField')
- ->will($this->returnValue('Model.id'));
- $Model
- ->expects($this->at($i++))
- ->method('find')
- ->with('count', $query);
-
- $i = 0;
- $Action = $this
- ->getMockBuilder('EditCrudAction')
- ->disableOriginalConstructor()
- ->setMethods(array('_model', '_getFindMethod', '_trigger'))
- ->getMock();
- $Action
- ->expects($this->at($i++))
- ->method('_model')
- ->will($this->returnValue($Model));
- $Action
- ->expects($this->never())
- ->method('_getFindMethod');
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('beforeFind', $findParams)
- ->will($this->returnValue(new CrudSubject($findParams)));
-
- $this->setReflectionClassInstance($Action);
- $this->callProtectedMethod('_findRecord', array(1, 'count'), $Action);
- }
-
-/**
- * testPutSetsIdFromUrl
- *
- * @return void
- */
- public function testPutSetsIdFromUrl() {
- $query = array('conditions' => array('Model.id' => 1));
- $findParams = array('findMethod' => 'count', 'query' => $query);
-
- $data = array('Model' => array('some' => 'data'));
-
- $Request = $this->getMock('CakeRequest');
- $Request->data = $data;
- $Request->params['pass'][0] = 1;
-
- $Controller = $this
- ->getMockBuilder('Controller')
- ->disableOriginalConstructor()
- ->getMock();
-
- $Model = $this
- ->getMockBuilder('Model')
- ->setMethods(array('saveAssociated', 'find', 'escapeField'))
- ->setConstructorArgs(array(array('name' => 'Model')))
- ->getMock();
-
- $i = $j = 0;
- $Action = $this
- ->getMockBuilder('EditCrudAction')
- ->disableOriginalConstructor()
- ->setMethods(array('_validateId', '_request', '_model', '_findRecord', '_trigger', 'setFlash'))
- ->getMock();
- $Action
- ->expects($this->at($i++))
- ->method('_validateId')
- ->will($this->returnValue(true));
- $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('_findRecord')
- ->will($this->returnValue(true));
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('beforeSave', array('id' => 1))
- ->will($this->returnValue(new CrudSubject(array('id' => 1))));
- $Model
- ->expects($this->at($j++))
- ->method('saveAssociated')
- ->with(array('Model' => array('id' => 1, 'some' => 'data')), array('validate' => 'first', 'atomic' => true));
-
- $this->setReflectionClassInstance($Action);
- $this->callProtectedMethod('_put', array(1), $Action);
- }
-
-/**
- * testPutSetsIdFromUrlWithAbreviatedData
- *
- * @return void
- */
- public function testPutSetsIdFromUrlWithAbreviatedData() {
- $query = array('conditions' => array('Model.id' => 1));
- $findParams = array('findMethod' => 'count', 'query' => $query);
-
- $data = array('some' => 'data');
-
- $Request = $this->getMock('CakeRequest');
- $Request->data = $data;
- $Request->params['pass'][0] = 1;
-
- $Controller = $this
- ->getMockBuilder('Controller')
- ->disableOriginalConstructor()
- ->getMock();
-
- $Model = $this
- ->getMockBuilder('Model')
- ->setMethods(array('saveAssociated', 'find', 'escapeField'))
- ->setConstructorArgs(array(array('name' => 'Model')))
- ->getMock();
-
- $i = $j = 0;
- $Action = $this
- ->getMockBuilder('EditCrudAction')
- ->disableOriginalConstructor()
- ->setMethods(array('_validateId', '_request', '_model', '_findRecord', '_trigger', 'setFlash'))
- ->getMock();
- $Action
- ->expects($this->at($i++))
- ->method('_validateId')
- ->will($this->returnValue(true));
- $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('_findRecord')
- ->will($this->returnValue(true));
- $Action
- ->expects($this->at($i++))
- ->method('_trigger')
- ->with('beforeSave', array('id' => 1))
- ->will($this->returnValue(new CrudSubject(array('id' => 1))));
- $Model
- ->expects($this->at($j++))
- ->method('saveAssociated')
- ->with(array('id' => 1, 'some' => 'data'), array('validate' => 'first', 'atomic' => true));
-
- $this->setReflectionClassInstance($Action);
- $this->callProtectedMethod('_put', array(1), $Action);
- }
-
-/**
- * test_validateId
- *
- * @return void
- */
- public function test_validateId() {
- $Request = $this->getMock('CakeRequest');
- $Request->data = null;
- $Request->params['pass'][0] = 1;
-
- $i = $j = 0;
- $Action = $this
- ->getMockBuilder('EditCrudAction')
- ->disableOriginalConstructor()
- ->setMethods(array('_request', '_model', '_trigger', 'message'))
- ->getMock();
- $Action->config('validateId', false);
- $Action
- ->expects($this->at($i++))
- ->method('_request')
- ->will($this->returnValue($Request));
-
- $this->setReflectionClassInstance($Action);
- $return = $this->callProtectedMethod('_validateId', array(1), $Action);
- $this->assertTrue($return, 'If there\'s no data, there should be no data check');
- }
-
-/**
- * test_validateIdMatches
- *
- * @return void
- */
- public function test_validateIdMatches() {
- $Request = $this->getMock('CakeRequest');
- $Request->data = array('Model' => array('id' => '1'));
- $Request->params['pass'][0] = 1;
-
- $Model = $this
- ->getMockBuilder('Model')
- ->setMethods(array('saveAssociated', 'find', 'escapeField'))
- ->setConstructorArgs(array(array('name' => 'Model')))
- ->getMock();
-
- $i = $j = 0;
- $Action = $this
- ->getMockBuilder('EditCrudAction')
- ->disableOriginalConstructor()
- ->setMethods(array('_request', '_model', '_trigger', 'message'))
- ->getMock();
- $Action->config('validateId', false);
- $Action
- ->expects($this->at($i++))
- ->method('_request')
- ->will($this->returnValue($Request));
- $Action
- ->expects($this->at($i++))
- ->method('_model')
- ->will($this->returnValue($Model));
-
- $this->setReflectionClassInstance($Action);
- $return = $this->callProtectedMethod('_validateId', array(1), $Action);
- $this->assertTrue($return, 'If there\'s data and it matches, there should be no exception');
- }
-
-/**
- * test_validateIdManipulated
- *
- * @expectedException BadRequestException
- * @expectedExceptionMessage Invalid id
- * @expectedExceptionCode 400
- *
- * @return void
- */
- public function test_validateIdManipulated() {
- $data = array('Model' => array('id' => 'manipulated', 'some' => 'data'));
-
- $Request = new CakeRequest();
- $Request->data = $data;
- $Request->params['pass'][0] = 1;
-
- $Model = $this
- ->getMockBuilder('Model')
- ->setMethods(array('saveAssociated', 'find', 'escapeField'))
- ->setConstructorArgs(array(array('name' => 'Model')))
- ->getMock();
-
- $i = $j = 0;
- $Action = $this
- ->getMockBuilder('EditCrudAction')
- ->disableOriginalConstructor()
- ->setMethods(array('_request', '_model', '_trigger', 'message'))
- ->getMock();
- $Action->config('validateId', false);
- $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('invalidId', array('id' => 'manipulated'));
- $Action
- ->expects($this->at($i++))
- ->method('message')
- ->with('invalidId')
- ->will($this->returnValue(array('class' => 'BadRequestException', 'code' => 400, 'text' => 'Invalid id')));
-
- $this->setReflectionClassInstance($Action);
- $this->callProtectedMethod('_validateId', array(1), $Action);
- }
-
-/**
- * test_validateIdManipulatedShortData
- *
- * @expectedException BadRequestException
- * @expectedExceptionMessage Invalid id
- * @expectedExceptionCode 400
- *
- * @return void
- */
- public function test_validateIdManipulatedShortData() {
- $data = array('id' => 'manipulated', 'some' => 'data');
-
- $Request = new CakeRequest();
- $Request->data = $data;
- $Request->params['pass'][0] = 1;
-
- $Model = $this
- ->getMockBuilder('Model')
- ->setMethods(array('saveAssociated', 'find', 'escapeField'))
- ->setConstructorArgs(array(array('name' => 'Model')))
- ->getMock();
-
- $i = $j = 0;
- $Action = $this
- ->getMockBuilder('EditCrudAction')
- ->disableOriginalConstructor()
- ->setMethods(array('_request', '_model', '_trigger', 'message'))
- ->getMock();
- $Action->config('validateId', false);
- $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('invalidId', array('id' => 'manipulated'));
- $Action
- ->expects($this->at($i++))
- ->method('message')
- ->with('invalidId')
- ->will($this->returnValue(array('class' => 'BadRequestException', 'code' => 400, 'text' => 'Invalid id')));
-
- $this->setReflectionClassInstance($Action);
- $this->callProtectedMethod('_validateId', array(1), $Action);
- }
-
-/**
- * Verify that _injectPrimaryKey is called, and the result is passed to saveAssociated
- *
- * @return void
- */
- public function test_injectPrimaryKeyIsCalled() {
- $CrudSubject = new CrudSubject();
-
- $Request = $this->getMock('CakeRequest');
- $Request->data = array('fake', 'input', 'data');
-
- $Model = $this
- ->getMockBuilder('Model')
- ->setMethods(array('saveAssociated', 'find', 'escapeField'))
- ->setConstructorArgs(array(array('name' => 'Model')))
- ->getMock();
- $Model
- ->expects($this->any())
- ->method('saveAssociated')
- ->with(array('id' => 1, '_injectPrimaryKey' => 'return'))
- ->will($this->returnValue(false));
-
- $i = 0;
- $Action = $this
- ->getMockBuilder('EditCrudAction')
- ->disableOriginalConstructor()
- ->setMethods(array('_validateId', '_request', '_model', '_findRecord', '_injectPrimaryKey', 'setFlash', '_trigger'))
- ->getMock();
- $Action
- ->expects($this->any())
- ->method('_validateId')
- ->will($this->returnValue(true));
- $Action
- ->expects($this->any())
- ->method('_request')
- ->will($this->returnValue($Request));
- $Action
- ->expects($this->any())
- ->method('_model')
- ->will($this->returnValue($Model));
- $Action
- ->expects($this->any())
- ->method('_findRecord')
- ->will($this->returnValue(true));
- $Action
- ->expects($this->once())
- ->method('_injectPrimaryKey')
- ->will($this->returnValue(array('id' => 1, '_injectPrimaryKey' => 'return')));
-
- $this->setReflectionClassInstance($Action);
- $this->callProtectedMethod('_put', array(1), $Action);
- }
-
-/**
- * test_injectPrimaryKey
- *
- * Check that the model id is injected into the right place
- *
- * @dataProvider idInjectionProvider
- * @param array $data
- * @param array $expectation
- */
- public function test_injectPrimaryKey($data, $expectation = null) {
- if (!$expectation) {
- $expectation = $data;
- }
-
- $Model = $this
- ->getMockBuilder('Model')
- ->setMethods(array('saveAssociated', 'find', 'escapeField'))
- ->setConstructorArgs(array(array('name' => 'Model')))
- ->getMock();
-
- $Action = new EditCrudAction(new CrudSubject());
-
- $this->setReflectionClassInstance($Action);
- $return = $this->callProtectedMethod('_injectPrimaryKey', array($data, 1, $Model), $Action);
- $this->assertSame($expectation, $return, '"id" should be injected in the right place in the save data');
- }
-
-/**
- * idInjectionProvider
- *
- * Returns sets of data to use in tests.
- * input
- * expected result (optional, uses input if absent)
- *
- * @return array
- */
- public function idInjectionProvider() {
- return array(
- array(
- array(),
- array('id' => 1)
- ),
- array(
- array('Model' => array('id' => 1, 'some' => 'update'))
- ),
- array(
- array('Model' => array('id' => 'cheating', 'some' => 'update')),
- array('Model' => array('id' => 1, 'some' => 'update'))
- ),
- array(
- array('Model' => array('some' => 'update')),
- array('Model' => array('some' => 'update', 'id' => 1))
- ),
- array(
- array('id' => 1, 'some' => 'update')
- ),
- array(
- array('some' => 'update'),
- array('some' => 'update', 'id' => 1)
- ),
- array(
- array('something' => 'else', 'Model' => array('some' => 'update')),
- array('something' => 'else', 'Model' => array('some' => 'update', 'id' => 1)),
- ),
- array(
- array('Category' => array('Category' => array(1))),
- array(
- 'Category' => array('Category' => array(1)),
- 'Model' => array('id' => 1)
- ),
- ),
-
- );
- }
-
-/**
- * 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'] = 'edit';
- $Request->data = array('_add' => 'something');
-
- $Controller->__construct($Request, new CakeResponse);
-
- $Crud->__construct(new ComponentCollection);
- $Crud->initialize($Controller);
- $Crud->mapAction('edit', 'edit');
-
- $Crud->addListener('redirect');
- $Crud->listener('redirect');
-
- $Action = $Crud->action('edit');
-
- $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' => 'add');
- $this->assertEquals($expected, $CrudSubject->url);
- }
-
-/**
- * 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'] = 'edit';
- $Request->data = array('_edit' => 'something');
-
- $Controller->__construct($Request, new CakeResponse);
-
- $Crud->__construct(new ComponentCollection);
- $Crud->initialize($Controller);
- $Crud->mapAction('edit', 'edit');
-
- $Crud->addListener('redirect');
- $Crud->listener('redirect');
-
- $Action = $Crud->action('edit');
-
- $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);
- }
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/IndexCrudActionTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/IndexCrudActionTest.php
deleted file mode 100644
index 04af1ee47..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/IndexCrudActionTest.php
+++ /dev/null
@@ -1,341 +0,0 @@
-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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/ViewCrudActionTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/ViewCrudActionTest.php
deleted file mode 100644
index 16330af00..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Action/ViewCrudActionTest.php
+++ /dev/null
@@ -1,365 +0,0 @@
- 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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudActionTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudActionTest.php
deleted file mode 100644
index c009b0b53..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudActionTest.php
+++ /dev/null
@@ -1,989 +0,0 @@
- 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');
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudListenerTest.php
deleted file mode 100644
index cb89eb253..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudListenerTest.php
+++ /dev/null
@@ -1,19 +0,0 @@
-assertTrue(true);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudSubjectTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudSubjectTest.php
deleted file mode 100644
index ab062d7fb..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/CrudSubjectTest.php
+++ /dev/null
@@ -1,83 +0,0 @@
-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');
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiFieldFilterListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiFieldFilterListenerTest.php
deleted file mode 100644
index 364a98f34..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiFieldFilterListenerTest.php
+++ /dev/null
@@ -1,438 +0,0 @@
-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']);
- }
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiListenerTest.php
deleted file mode 100644
index 5270574f8..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiListenerTest.php
+++ /dev/null
@@ -1,1450 +0,0 @@
-_config = Configure::read();
- }
-
- public function tearDown() {
- parent::tearDown();
- Configure::write($this->_config);
- CakePlugin::unload('TestPlugin');
- }
-
-/**
- * testBeforeHandle
- *
- * @return void
- */
- public function testBeforeHandle() {
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('_request', 'registerExceptionHandler', '_checkRequestMethods'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request = $this
- ->getMockBuilder('CakeRequest')
- ->setMethods(array('is'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $listener
- ->expects($this->at(0))
- ->method('_request')
- ->will($this->returnValue($request));
- $request
- ->expects($this->once())
- ->method('is')
- ->with('api')
- ->will($this->returnValue(true));
- $listener
- ->expects($this->at(1))
- ->method('registerExceptionHandler');
- $listener
- ->expects($this->at(1))
- ->method('_checkRequestMethods');
-
- $listener->beforeHandle(new CakeEvent('Crud.beforeHandle'));
- }
-
-/**
- * testSetup
- *
- * @return void
- */
- public function testSetup() {
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('setupDetectors', 'registerExceptionHandler'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $listener
- ->expects($this->at(0))
- ->method('setupDetectors');
-
- $listener
- ->expects($this->at(1))
- ->method('registerExceptionHandler');
-
- $listener->setup();
- }
-
-/**
- * testBeforeHandleNotApi
- *
- * @return void
- */
- public function testBeforeHandleNotApi() {
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('_request', '_controller', 'registerExceptionHandler', '_checkRequestMethods'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request = $this
- ->getMockBuilder('CakeRequest')
- ->setMethods(array('is'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('getEventManager'))
- ->getMock();
-
- $manager = $this
- ->getMockBuilder('EventManager')
- ->setMethods(array('detach'))
- ->getMock();
-
- $listener
- ->expects($this->at(0))
- ->method('_request')
- ->will($this->returnValue($request));
- $request
- ->expects($this->once())
- ->method('is')
- ->with('api')
- ->will($this->returnValue(false));
- $listener
- ->expects($this->at(1))
- ->method('_controller')
- ->will($this->returnValue($controller));
- $controller
- ->expects($this->at(0))
- ->method('getEventManager')
- ->will($this->returnValue($manager));
- $manager
- ->expects($this->at(0))
- ->method('detach')
- ->with($listener, 'Crud.setFlash');
- $manager
- ->expects($this->at(1))
- ->method('detach')
- ->with($listener, 'Crud.beforeRender');
- $manager
- ->expects($this->at(2))
- ->method('detach')
- ->with($listener, 'Crud.beforeRedirect');
-
- $listener
- ->expects($this->never())
- ->method('registerExceptionHandler');
- $listener
- ->expects($this->never())
- ->method('_checkRequestMethods');
-
- $listener->beforeHandle(new CakeEvent('Crud.beforeHandle'));
- }
-
-/**
- * Test response method
- *
- * @return void
- */
- public function testResponse() {
- $request = $this->getMock('CakeRequest', array('is'));
- $response = $this->getMock('CakeResponse');
-
- $action = $this->getMock('IndexCrudAction', array('config'), array(new CrudSubject()));
-
- $subject = $this->getMock('CrudSubject');
- $subject->success = true;
-
- $event = new CakeEvent('Crud.afterSave', $subject);
-
- $i = 0;
-
- $listener = $this->getMock('ApiListener', array('_request', '_action', 'render'), array($subject));
- $listener
- ->expects($this->at($i++))
- ->method('_action')
- ->with()
- ->will($this->returnValue($action));
- $action
- ->expects($this->at(0))
- ->method('config')
- ->with('api.success')
- ->will($this->returnValue(array('code' => 200)));
- $listener
- ->expects($this->at($i++))
- ->method('render')
- ->with($subject)
- ->will($this->returnValue($response));
- $response
- ->expects($this->at(0))
- ->method('statusCode')
- ->with(200);
-
- $result = $listener->respond($event);
- $this->assertSame($response, $result);
- }
-
-/**
- * Test response method with exception config
- *
- * @return void
- */
- public function testResponseWithExceptionConfig() {
- $request = $this->getMock('CakeRequest', array('is'));
- $response = $this->getMock('CakeResponse');
-
- $action = $this->getMock('IndexCrudAction', array('config'), array(new CrudSubject()));
-
- $subject = $this->getMock('CrudSubject');
- $subject->success = true;
-
- $event = new CakeEvent('Crud.afterSave', $subject);
-
- $i = 0;
-
- $listener = $this->getMock('ApiListener', array('_request', '_action', 'render', '_exceptionResponse'), array($subject));
- $listener
- ->expects($this->at($i++))
- ->method('_action')
- ->with()
- ->will($this->returnValue($action));
- $action
- ->expects($this->at(0))
- ->method('config')
- ->with('api.success')
- ->will($this->returnValue(array('exception' => true)));
- $listener
- ->expects($this->at($i++))
- ->method('_exceptionResponse')
- ->with(true);
- $listener
- ->expects($this->never())
- ->method('render');
- $response
- ->expects($this->never())
- ->method('statusCode');
-
- $listener->respond($event);
- }
-
-/**
- * Test default configuration
- *
- * @return void
- */
- public function testDefaultConfiguration() {
- $listener = new ApiListener(new CrudSubject());
- $expected = 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
- )
- );
- $result = $listener->config();
- $this->assertEquals($expected, $result);
- }
-
-/**
- * Tests implemented events
- *
- * @return void
- */
- public function testImplementeEvents() {
- $subject = $this->getMock('CrudSubject');
- $apiListener = new ApiListener($subject);
- $expected = 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)
- );
- $this->assertEquals($expected, $apiListener->implementedEvents());
- }
-
-/**
- * Data provider for test_exceptionResponse
- *
- * @return array
- */
- public function data_exceptionResponse() {
- return array(
- 'default configuration' => array(
- array(),
- 'BadRequestException',
- 'Unknown error',
- 0
- ),
-
- 'change exception class' => array(
- array('class' => 'CakeException'),
- 'CakeException',
- 'Unknown error',
- 0
- ),
-
- 'change exception code' => array(
- array('code' => 10),
- 'BadRequestException',
- 'Unknown error',
- 10
- ),
-
- 'change exception message' => array(
- array('message' => 'epic message'),
- 'BadRequestException',
- 'epic message',
- 10
- ),
-
- 'Validate case #1 - no validation errors' => array(
- array('class' => 'CrudValidationException', 'type' => 'validate'),
- 'CrudValidationException',
- '0 validation errors occurred',
- 0
- ),
-
- 'Validate case #2 - one validation error' => array(
- array('class' => 'CrudValidationException', 'type' => 'validate'),
- 'CrudValidationException',
- 'A validation error occurred',
- 0,
- array(array('id' => 'hello world'))
- ),
-
- 'Validate case #3 - two validation errors' => array(
- array('class' => 'CrudValidationException', 'type' => 'validate'),
- 'CrudValidationException',
- '2 validation errors occurred',
- 0,
- array(array('id' => 'hello world', 'name' => 'fail me'))
- )
- );
- }
-
-/**
- * Test _exceptionResponse
- *
- * @dataProvider data_exceptionResponse
- * @param array $apiConfig
- * @param string $exceptionClass
- * @param string $exceptionMessage
- * @param integer $exceptionCode
- * @param array $validationErrors
- * @return void
- */
- public function test_exceptionResponse($apiConfig, $exceptionClass, $exceptionMessage, $exceptionCode, $validationErrors = array()) {
- $listener = $this->getMock('ApiListener', array('_validationErrors'), array(new CrudSubject()));
-
- if (isset($apiConfig['type']) && $apiConfig['type'] === 'validate') {
- $listener->expects($this->once())->method('_validationErrors')->with()->will($this->returnValue($validationErrors));
- } else {
- $listener->expects($this->never())->method('_validationErrors');
- }
-
- $this->expectException($exceptionClass, $exceptionMessage, $exceptionCode);
-
- $this->setReflectionClassInstance($listener);
- $this->callProtectedMethod('_exceptionResponse', array($apiConfig), $listener);
- }
-
-/**
- * Test render
- *
- * @return void
- */
- public function testRender() {
- $listener = $this->getMockBuilder('ApiListener')
- ->setMethods(array('injectViewClasses', '_ensureSuccess', '_ensureData', '_ensureSerialize', '_controller'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $subject = new CrudSubject();
-
- $requestHandler = $this->getMockBuilder('RequestHandlerComponent')
- ->setMethods(array('renderAs'))
- ->disableOriginalConstructor()
- ->getMock();
- $controller = $this->getMockBuilder('Controller')
- ->setMethods(array('render'))
- ->disableOriginalConstructor()
- ->getMock();
- $controller->RequestHandler = $requestHandler;
- $controller->RequestHandler->ext = 'json';
-
- $i = 0;
- $listener
- ->expects($this->at($i++))
- ->method('injectViewClasses')
- ->with();
- $listener
- ->expects($this->at($i++))
- ->method('_ensureSuccess')
- ->with($subject);
- $listener
- ->expects($this->at($i++))
- ->method('_ensureData')
- ->with($subject);
- $listener
- ->expects($this->at($i++))
- ->method('_ensureSerialize')
- ->with();
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->with()
- ->will($this->returnValue($controller));
- $requestHandler
- ->expects($this->once())
- ->method('renderAs')
- ->with($controller, 'json');
- $controller
- ->expects($this->once())
- ->method('render')
- ->with();
-
- $listener->render($subject);
- }
-
-/**
- * test_ensureSerializeWithViewVar
- *
- * @return void
- */
- public function test_ensureSerializeWithViewVar() {
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('_action', '_controller'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('set'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $action = $this
- ->getMockBuilder('IndexCrudAction')
- ->setMethods(array('config', 'viewVar'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $i = 0;
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
- $listener
- ->expects($this->at($i++))
- ->method('_action')
- ->will($this->returnValue($action));
- $action
- ->expects($this->at(0))
- ->method('viewVar')
- ->will($this->returnValue('items'));
- $controller
- ->expects($this->once())
- ->method('set')
- ->with('_serialize', array('success', 'data' => 'items'));
-
- $this->setReflectionClassInstance($listener);
- $this->callProtectedMethod('_ensureSerialize', array(), $listener);
- }
-
-/**
- * test_ensureSerializeAlreadySet
- *
- * @return void
- */
- public function test_ensureSerializeAlreadySet() {
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('_action', '_controller'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('set'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $controller->viewVars['_serialize'] = 'hello world';
-
- $action = $this
- ->getMockBuilder('IndexCrudAction')
- ->setMethods(array('config', 'viewVar'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $i = 0;
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
- $listener
- ->expects($this->never())
- ->method('_action');
- $action
- ->expects($this->never())
- ->method('viewVar');
- $controller
- ->expects($this->never())
- ->method('set');
-
- $this->setReflectionClassInstance($listener);
- $this->callProtectedMethod('_ensureSerialize', array(), $listener);
- }
-
-/**
- * test_ensureSerializeWithViewVarChanged
- *
- * @return void
- */
- public function test_ensureSerializeWithViewVarChanged() {
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('_action', '_controller'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('set'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $action = $this
- ->getMockBuilder('IndexCrudAction')
- ->setMethods(array('config', 'viewVar'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $i = 0;
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
- $listener
- ->expects($this->at($i++))
- ->method('_action')
- ->will($this->returnValue($action));
- $action
- ->expects($this->at(0))
- ->method('viewVar')
- ->will($this->returnValue('helloWorld'));
- $controller
- ->expects($this->once())
- ->method('set')
- ->with('_serialize', array('success', 'data' => 'helloWorld'));
-
- $this->setReflectionClassInstance($listener);
- $this->callProtectedMethod('_ensureSerialize', array(), $listener);
- }
-
-/**
- * test_ensureSerializeWithoutViewVar
- *
- * @return void
- */
- public function test_ensureSerializeWithoutViewVar() {
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('_action', '_controller'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('set'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $action = $this
- ->getMockBuilder('AddCrudAction')
- ->setMethods(array('config'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $i = 0;
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
- $listener
- ->expects($this->at($i++))
- ->method('_action')
- ->will($this->returnValue($action));
- $controller
- ->expects($this->once())
- ->method('set')
- ->with('_serialize', array('success', 'data'));
-
- $this->setReflectionClassInstance($listener);
- $this->callProtectedMethod('_ensureSerialize', array(), $listener);
- }
-
-/**
- * test_ensureSuccess
- *
- * @return void
- */
- public function test_ensureSuccess() {
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('_controller'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $subject = new CrudSubject(array('success' => true));
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('set'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $i = 0;
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
- $controller
- ->expects($this->once())
- ->method('set')
- ->with('success', true);
-
- $this->setReflectionClassInstance($listener);
- $this->callProtectedMethod('_ensureSuccess', array($subject), $listener);
- }
-
-/**
- * test_ensureData
- *
- * @return void
- */
- public function test_ensureData() {
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('_controller', '_action'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('set'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $action = $this
- ->getMockBuilder('CrudAction')
- ->setMethods(array('config'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $subject = new CrudSubject(array('success' => true));
-
- $config = array();
-
- $i = 0;
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
- $listener
- ->expects($this->at($i++))
- ->method('_action')
- ->will($this->returnValue($action));
- $action
- ->expects($this->at(0))
- ->method('config')
- ->with('api.success')
- ->will($this->returnValue($config));
- $controller
- ->expects($this->once())
- ->method('set')
- ->with('data', array());
-
- $this->setReflectionClassInstance($listener);
- $result = $this->callProtectedMethod('_ensureData', array($subject), $listener);
- }
-
-/**
- * test_ensureDataSubject
- *
- * @return void
- */
- public function test_ensureDataSubject() {
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('_controller', '_action'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('set'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $action = $this
- ->getMockBuilder('CrudAction')
- ->setMethods(array('config'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $subject = new CrudSubject(array('success' => true, 'id' => 1, 'modelClass' => 'MyModel'));
-
- $config = array('data' => array(
- 'subject' => array(
- '{modelClass}.id' => 'id',
- 'modelClass'
- )
- ));
-
- $i = 0;
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
- $listener
- ->expects($this->at($i++))
- ->method('_action')
- ->will($this->returnValue($action));
- $action
- ->expects($this->at(0))
- ->method('config')
- ->with('api.success')
- ->will($this->returnValue($config));
- $controller
- ->expects($this->once())
- ->method('set')
- ->with('data', array('modelClass' => 'MyModel', 'MyModel' => array('id' => 1)));
-
- $this->setReflectionClassInstance($listener);
- $result = $this->callProtectedMethod('_ensureData', array($subject), $listener);
- }
-
-/**
- * test_ensureDataRaw
- *
- * @return void
- */
- public function test_ensureDataRaw() {
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('_controller', '_action'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('set'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $action = $this
- ->getMockBuilder('CrudAction')
- ->setMethods(array('config'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $subject = new CrudSubject(array('success' => true, 'id' => 1, 'modelClass' => 'MyModel'));
-
- $config = array('data' => array('raw' => array('{modelClass}.id' => 1)));
-
- $i = 0;
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
- $listener
- ->expects($this->at($i++))
- ->method('_action')
- ->will($this->returnValue($action));
- $action
- ->expects($this->at(0))
- ->method('config')
- ->with('api.success')
- ->will($this->returnValue($config));
- $controller
- ->expects($this->once())
- ->method('set')
- ->with('data', array('MyModel' => array('id' => 1)));
-
- $this->setReflectionClassInstance($listener);
- $result = $this->callProtectedMethod('_ensureData', array($subject), $listener);
- }
-
-/**
- * test_ensureDataError
- *
- * @return void
- */
- public function test_ensureDataError() {
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('_controller', '_action'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('set'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $action = $this
- ->getMockBuilder('CrudAction')
- ->setMethods(array('config'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $subject = new CrudSubject(array('success' => false));
-
- $config = array();
-
- $i = 0;
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
- $listener
- ->expects($this->at($i++))
- ->method('_action')
- ->will($this->returnValue($action));
- $action
- ->expects($this->at(0))
- ->method('config')
- ->with('api.error')
- ->will($this->returnValue($config));
- $controller
- ->expects($this->once())
- ->method('set')
- ->with('data', array());
-
- $this->setReflectionClassInstance($listener);
- $result = $this->callProtectedMethod('_ensureData', array($subject), $listener);
- }
-
-/**
- * test_ensureDataExists
- *
- * @return void
- */
- public function test_ensureDataExists() {
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('_controller', '_action'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('set'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $controller->viewVars['data'] = true;
-
- $subject = new CrudSubject();
-
- $config = array();
-
- $i = 0;
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
- $listener
- ->expects($this->never())
- ->method('_action');
- $controller
- ->expects($this->never())
- ->method('set');
-
- $this->setReflectionClassInstance($listener);
- $result = $this->callProtectedMethod('_ensureData', array($subject), $listener);
- }
-
-/**
- * test_ensureSuccessAlreadySet
- *
- * @return void
- */
- public function test_ensureSuccessAlreadySet() {
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('_controller'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $subject = new CrudSubject(array('success' => true));
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('set'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $controller->viewVars['success'] = true;
-
- $i = 0;
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
- $controller
- ->expects($this->never())
- ->method('set');
-
- $this->setReflectionClassInstance($listener);
- $this->callProtectedMethod('_ensureSuccess', array($subject), $listener);
- }
-
-/**
- * testFlashMessageSupressed
- *
- * The API listener should suppress flash messages
- * if the request is "API"
- *
- * @return void
- */
- public function testFlashMessageSupressed() {
- $Request = new CakeRequest();
- $Request->addDetector('api', array('callback' => function() {
- return true;
- }));
-
- $subject = new CrudSubject(array('request' => $Request));
-
- $apiListener = new ApiListener($subject);
-
- $event = new CakeEvent('Crud.setFlash', $subject);
- $apiListener->setFlash($event);
-
- $stopped = $event->isStopped();
- $this->assertTrue($stopped, 'Set flash event is expected to be stopped');
- }
-
-/**
- * Data provider for test_expandPath
- *
- * @return array
- */
- public function data_expandPath() {
- return array(
- 'simple string' => array(
- new CrudSubject(array('modelClass' => 'MyModel')),
- '{modelClass}.id',
- 'MyModel.id'
- ),
-
- 'string and integer' => array(
- new CrudSubject(array('modelClass' => 'MyModel', 'id' => 1)),
- '{modelClass}.{id}',
- 'MyModel.1'
- ),
-
- 'ignore non scalar' => array(
- new CrudSubject(array('modelClass' => 'MyModel', 'complex' => new StdClass)),
- '{modelClass}.{id}',
- 'MyModel.{id}'
- ),
- );
- }
-
-/**
- * test_expandPath
- *
- * @dataProvider data_expandPath
- * @return void
- */
- public function test_expandPath($subject, $path, $expected) {
- $listener = new ApiListener(new CrudSubject());
-
- $this->setReflectionClassInstance($listener);
- $result = $this->callProtectedMethod('_expandPath', array($subject, $path), $listener);
- $this->assertSame($expected, $result);
- }
-
-/**
- * testSetupDetectors
- *
- * @return void
- */
- public function testSetupDetectors() {
- $detectors = array('xml' => array(), 'json' => array());
-
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('_request', 'config'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request = $this
- ->getMockBuilder('CakeRequest')
- ->setMethods(array('addDetector'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $i = 0;
- $listener
- ->expects($this->at($i++))
- ->method('_request')
- ->will($this->returnValue($request));
- $listener
- ->expects($this->at($i++))
- ->method('config')
- ->with('detectors')
- ->will($this->returnValue($detectors));
-
- $r = 0;
- foreach ($detectors as $name => $config) {
- $request
- ->expects($this->at($r++))
- ->method('addDetector')
- ->with($name);
- }
-
- $request
- ->expects($this->at($r++))
- ->method('addDetector')
- ->with('api');
-
- $listener->setupDetectors();
- }
-
-/**
- * testSetupDetectorsIntigration
- *
- * @return void
- */
- public function testSetupDetectorsIntigration() {
- $detectors = array(
- 'json' => array('ext' => 'json', 'accepts' => 'application/json'),
- 'xml' => array('ext' => 'xml', 'accepts' => 'text/xml')
- );
-
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('_request', 'config'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request = $this
- ->getMockBuilder('CakeRequest')
- ->setMethods(array('accepts'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $i = 0;
- $listener
- ->expects($this->at($i++))
- ->method('_request')
- ->will($this->returnValue($request));
- $listener
- ->expects($this->at($i++))
- ->method('config')
- ->with('detectors')
- ->will($this->returnValue($detectors));
-
- $listener->setupDetectors();
-
- // Test with "ext"
- foreach ($detectors as $name => $configuration) {
- $request->params['ext'] = $configuration['ext'];
- $this->assertTrue($request->is($name));
- }
-
- $request->params['ext'] = null;
-
- // Test with "accepts"
- $r = 0;
- foreach ($detectors as $name => $configuration) {
- $request
- ->expects($this->at($r++))
- ->method('accepts')
- ->with($configuration['accepts'])
- ->will($this->returnValue(true));
- }
-
- foreach ($detectors as $name => $config) {
- $this->assertTrue($request->is($name));
- }
-
- $request->params['ext'] = 'xml';
- $this->assertTrue($request->is('api'));
-
- $request->params['ext'] = null;
- $this->assertFalse($request->is('api'));
- }
-
-/**
- * testRegisterExceptionHandler with Api request
- *
- * @return void
- */
- public function testRegisterExceptionHandlerWithApi() {
- $listener = $this->getMockBuilder('ApiListener')
- ->setMethods(array('_request'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request = $this->getMockBuilder('CakeRequest')
- ->setMethods(array('is'))
- ->disableOriginalConstructor()
- ->getMock();
- $request
- ->expects($this->at(0))
- ->method('is')
- ->with('api')
- ->will($this->returnValue(true));
-
- $listener
- ->expects($this->once())
- ->method('_request')
- ->with()
- ->will($this->returnValue($request));
-
- $listener->registerExceptionHandler();
-
- $expected = 'Crud.CrudExceptionRenderer';
- $result = Configure::read('Exception.renderer');
- $this->assertEquals($expected, $result);
- }
-
-
-/**
- * testRegisterExceptionHandler without Api request
- *
- * @return void
- */
- public function testRegisterExceptionHandlerWithoutApi() {
- $listener = $this->getMockBuilder('ApiListener')
- ->setMethods(array('_request'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request = $this->getMockBuilder('CakeRequest')
- ->setMethods(array('is'))
- ->disableOriginalConstructor()
- ->getMock();
- $request
- ->expects($this->at(0))
- ->method('is')
- ->with('api')
- ->will($this->returnValue(false));
-
- $listener
- ->expects($this->once())
- ->method('_request')
- ->with()
- ->will($this->returnValue($request));
-
- $listener->registerExceptionHandler();
-
- $expected = 'ExceptionRenderer';
- $result = Configure::read('Exception.renderer');
- $this->assertEquals($expected, $result);
- }
-/**
- * data provider for test_checkRequestMethods
- *
- * @return array
- */
- public function data_checkRequestMethods() {
- return array(
- 'defaults' => array(
- array(),
- false,
- array()
- ),
- 'valid get' => array(
- array('methods' => array('get')),
- true,
- array('get' => true)
- ),
- 'invalid post' => array(
- array('methods' => array('post')),
- 'BadRequestException',
- array('post' => false)
- ),
- 'valid put' => array(
- array('methods' => array('post', 'get', 'put')),
- true,
- array('post' => false, 'get' => false, 'put' => true)
- )
- );
- }
-
-/**
- * test_checkRequestMethods
- *
- * @dataProvider data_checkRequestMethods
- * @return void
- */
- public function test_checkRequestMethods($apiConfig, $exception, $requestMethods) {
- $listener = $this
- ->getMockBuilder('ApiListener')
- ->setMethods(array('_action', '_request'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $action = $this
- ->getMockBuilder('CrudAction')
- ->setMethods(array('config'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request = $this
- ->getMockBuilder('CakeRequest')
- ->setMethods(array('is'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $listener
- ->expects($this->at(0))
- ->method('_action')
- ->will($this->returnValue($action));
- $action
- ->expects($this->at(0))
- ->method('config')
- ->with('api')
- ->will($this->returnValue($apiConfig));
-
- if (!empty($apiConfig['methods'])) {
- $listener
- ->expects($this->at(1))
- ->method('_request')
- ->will($this->returnValue($request));
-
- $r = 0;
- foreach ($requestMethods as $method => $bool) {
- $request
- ->expects($this->at($r++))
- ->method('is')
- ->with($method)
- ->will($this->returnValue($bool));
- }
- } else {
- $listener
- ->expects($this->never())
- ->method('_request');
- }
-
- if (is_string($exception)) {
- $this->expectException($exception);
- }
-
- $this->setReflectionClassInstance($listener);
- $result = $this->callProtectedMethod('_checkRequestMethods', array(), $listener);
-
- if (is_bool($exception)) {
- $this->assertEquals($exception, $result);
- }
- }
-
-/**
- * testMapResources
- *
- * Passing no argument, should map all of the app's controllers
- *
- * @return void
- */
- public function testMapResources() {
- $path = CAKE . 'Test' . DS . 'test_app' . DS . 'Controller' . DS;
- App::build(array(
- 'Controller' => array($path)
- ), App::RESET);
-
- Router::reload();
- Router::$routes = array();
-
- ApiListener::mapResources();
-
- $expected = $this->_getRouteExpectations();
- $return = $this->_currentRoutes();
-
- $this->assertSame($expected, $return, 'test_app contains several controllers - there should be rest routes for all of them');
- }
-
-/**
- * _getRouteExpectations
- *
- * A little helper function which returns routes expectations for all app controllers
- *
- * @return array
- */
- protected function _getRouteExpectations() {
- $routePatterns = array(
- 'GET index /{name}',
- 'GET view /{name}/:id',
- 'POST add /{name}',
- 'PUT edit /{name}/:id',
- 'DELETE delete /{name}/:id',
- 'POST edit /{name}/:id',
- );
-
- $expected = array();
- $controllers = App::objects('Controller');
- foreach ($controllers as $controller) {
- $controller = substr($controller, 0, - strlen('Controller'));
- $controller = Inflector::underscore($controller);
- if ($controller === 'app') {
- continue;
- }
-
- $routes = $routePatterns;
- foreach ($routes as &$route) {
- $route = str_replace('{name}', $controller, $route);
- }
-
- $expected = array_merge($expected, $routes);
- }
-
- return $expected;
- }
-
-/**
- * Passing a plugin name should map only for that plugin
- *
- * @return void
- */
- public function testMapResourcesPlugin() {
- $path = CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS;
- App::build(array(
- 'Plugin' => array($path)
- ), App::RESET);
- CakePlugin::load('TestPlugin');
-
- Router::reload();
- Router::$routes = array();
-
- ApiListener::mapResources('TestPlugin');
-
- $expected = array(
- 'GET index /test_plugin/test_plugin',
- 'GET view /test_plugin/test_plugin/:id',
- 'POST add /test_plugin/test_plugin',
- 'PUT edit /test_plugin/test_plugin/:id',
- 'DELETE delete /test_plugin/test_plugin/:id',
- 'POST edit /test_plugin/test_plugin/:id',
- 'GET index /test_plugin/tests',
- 'GET view /test_plugin/tests/:id',
- 'POST add /test_plugin/tests',
- 'PUT edit /test_plugin/tests/:id',
- 'DELETE delete /test_plugin/tests/:id',
- 'POST edit /test_plugin/tests/:id',
- );
- $return = $this->_currentRoutes();
-
- $this->assertSame($expected, $return, 'test plugin contains a test plugin and tests controller');
- }
-
-/**
- * _currentRoutes
- *
- * Return current route definitions in a very simple format for comparison purposes
- *
- * @return array
- */
- protected function _currentRoutes() {
- $return = array();
-
- foreach (Router::$routes as $route) {
- $return[] = $route->defaults['[method]'] .
- ' ' . $route->defaults['action'] .
- ' ' . $route->template;
- }
-
- return $return;
- }
-
-/**
- * testViewClass
- *
- * Test that both set and get works
- *
- * @return void
- */
- public function testViewClass() {
- $apiListener = new ApiListener(new CrudSubject());
-
- $result = $apiListener->viewClass('json', 'Sample.ViewClass');
- $this->assertEquals($apiListener, $result, 'Setting a viewClass did not return the listener itself');
-
- $result = $apiListener->viewClass('json');
- $this->assertEquals('Sample.ViewClass', $result, 'The changed viewClass was not returned');
- }
-
-/**
- * testViewClassDefaults
- *
- * Test that the default viewClasses are as expected
- *
- * @return void
- */
- public function testViewClassDefaults() {
- $apiListener = new ApiListener(new CrudSubject());
-
- $result = $apiListener->config('viewClasses');
- $expected = array(
- 'json' => 'Crud.CrudJson',
- 'xml' => 'Crud.CrudXml'
- );
- $this->assertEquals($expected, $result, 'The default viewClasses setting has changed');
- }
-
-/**
- * testInjectViewClasses
- *
- * @return void
- */
- public function testInjectViewClasses() {
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('foo')) // need to mock *something* to make Controller::__set work
- ->disableOriginalConstructor()
- ->getMock();
-
- $controller->RequestHandler = $this->getMock('RequestHandler', array('viewClassMap'));
- $controller->RequestHandler->expects($this->at(0))->method('viewClassMap')->with('json', 'Crud.CrudJson');
- $controller->RequestHandler->expects($this->at(1))->method('viewClassMap')->with('xml', 'Crud.CrudXml');
-
- $apiListener = $this->getMock('ApiListener', array('_controller'), array(new CrudSubject()));
- $apiListener->expects($this->once())->method('_controller')->will($this->returnValue($controller));
- $apiListener->injectViewClasses();
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiPaginationListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiPaginationListenerTest.php
deleted file mode 100644
index 4bf1cc658..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiPaginationListenerTest.php
+++ /dev/null
@@ -1,167 +0,0 @@
-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));
- }
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiQueryLogListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiQueryLogListenerTest.php
deleted file mode 100644
index 2a96f4e20..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiQueryLogListenerTest.php
+++ /dev/null
@@ -1,196 +0,0 @@
-_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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiTransformationListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiTransformationListenerTest.php
deleted file mode 100644
index 902fe22bc..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ApiTransformationListenerTest.php
+++ /dev/null
@@ -1,1441 +0,0 @@
-getMockBuilder('ApiTransformationListener')
- ->setMethods(array('_request'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $expected = array('Crud.beforeRender' => array('callable' => 'beforeRender', 'priority' => 200));
- $result = $listener->implementedEvents();
- $this->assertSame($expected, $result);
- }
-
-/**
- * testBeforeRenderNoApiRequest
- *
- * @return void
- */
- public function testBeforeRenderNoApiRequest() {
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->setMethods(array('_request'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request = $this
- ->getMockBuilder('CakeRequest')
- ->setMethods(array('is'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request
- ->expects($this->once())
- ->method('is')
- ->with('api')
- ->will($this->returnValue(false));
-
- $listener
- ->expects($this->once())
- ->method('_request')
- ->will($this->returnValue($request));
-
- $listener
- ->expects($this->never())
- ->method('_controller');
-
- $listener
- ->expects($this->never())
- ->method('_action');
-
- $listener
- ->expects($this->never())
- ->method('_model');
-
- $listener
- ->expects($this->never())
- ->method('_setMethods');
-
- $listener
- ->expects($this->never())
- ->method('_changeNesting');
-
- $listener
- ->expects($this->never())
- ->method('_recurse');
-
- $this->assertTrue($listener->beforeRender());
- }
-
-/**
- * testBeforeRenderWithNestingChange
- *
- * @return void
- */
- public function testBeforeRenderWithNestingChange() {
- $i = 0;
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->setMethods(array('_request', '_controller', '_action', '_model', '_changeNesting', '_recurse', '_setMethods'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request = $this
- ->getMockBuilder('CakeRequest')
- ->setMethods(array('is'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request
- ->expects($this->once())
- ->method('is')
- ->with('api')
- ->will($this->returnValue(true));
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('set'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $action = $this
- ->getMockBuilder('IndexAction')
- ->setMethods(array('viewVar'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $model = $this
- ->getMockBuilder('Model')
- ->disableOriginalConstructor()
- ->getMock();
-
- $listener
- ->expects($this->at($i++))
- ->method('_request')
- ->will($this->returnValue($request));
-
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
-
- $listener
- ->expects($this->at($i++))
- ->method('_action')
- ->will($this->returnValue($action));
-
- $listener
- ->expects($this->at($i++))
- ->method('_setMethods');
-
- $listener
- ->expects($this->at($i++))
- ->method('_model')
- ->will($this->returnValue($model));
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => true,
- 'changeTime' => true,
- 'castNumbers' => true,
- 'keyMethods' => array(),
- 'valueMethods' => array(),
- 'replaceMap' => array()
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $success = true;
- $users = array(
- 0 => array(
- 'User' => array('id' => 5, 'name' => 'FriendsOfCake'),
- 'Profile' => array('id' => 987, 'twitter' => '@FriendsOfCake')
- ),
- 1 => array(
- 'User' => array('id' => 45, 'name' => 'CakePHP'),
- 'Profile' => array('id' => 123, 'twitter' => '@cakephp')
- ),
- );
-
- $controller->viewVars = compact('success', 'users');
-
- $action
- ->expects($this->once())
- ->method('viewVar')
- ->will($this->returnValue('users'));
-
- $model->alias = 'User';
-
- $result = array();
-
- $nested = array(
- 'id' => 5,
- 'name' => 'FriendsOfCake',
- 'Profile' => array('id' => 987, 'twitter' => '@FriendsOfCake')
- );
- $result[] = $nested;
-
- $listener
- ->expects($this->at($i++))
- ->method('_changeNesting')
- ->with($this->identicalTo($users[0]), 'User')
- ->will($this->returnValue($nested));
-
- $listener
- ->expects($this->at($i++))
- ->method('_recurse')
- ->with($nested);
-
- $nested = array(
- 'id' => 45,
- 'name' => 'CakePHP',
- 'Profile' => array('id' => 123, 'twitter' => '@cakephp')
- );
- $result[] = $nested;
-
- $listener
- ->expects($this->at($i++))
- ->method('_changeNesting')
- ->with($this->identicalTo($users[1]), 'User')
- ->will($this->returnValue($nested));
-
- $listener
- ->expects($this->at($i++))
- ->method('_recurse')
- ->with($nested);
-
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
-
- $controller
- ->expects($this->once())
- ->method('set')
- ->with('users', $result);
-
- $this->assertTrue($listener->beforeRender());
- }
-
-/**
- * testBeforeRenderWithoutNestingChange
- *
- * @return void
- */
- public function testBeforeRenderWithoutNestingChange() {
- $i = 0;
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->setMethods(array('_request', '_controller', '_action', '_model', '_changeNesting', '_recurse', '_setMethods'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request = $this
- ->getMockBuilder('CakeRequest')
- ->setMethods(array('is'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request
- ->expects($this->once())
- ->method('is')
- ->with('api')
- ->will($this->returnValue(true));
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('set'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $action = $this
- ->getMockBuilder('IndexAction')
- ->setMethods(array('viewVar'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $model = $this
- ->getMockBuilder('Model')
- ->disableOriginalConstructor()
- ->getMock();
-
- $listener
- ->expects($this->at($i++))
- ->method('_request')
- ->will($this->returnValue($request));
-
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
-
- $listener
- ->expects($this->at($i++))
- ->method('_action')
- ->will($this->returnValue($action));
-
- $listener
- ->expects($this->at($i++))
- ->method('_setMethods');
-
- $listener
- ->expects($this->at($i++))
- ->method('_model')
- ->will($this->returnValue($model));
-
- $settings = array(
- 'changeNesting' => false,
- 'changeKeys' => true,
- 'changeTime' => true,
- 'castNumbers' => true,
- 'keyMethods' => array(),
- 'valueMethods' => array(),
- 'replaceMap' => array()
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $success = true;
- $users = array(
- 0 => array(
- 'User' => array('id' => 5, 'name' => 'FriendsOfCake'),
- 'Profile' => array('id' => 987, 'twitter' => '@FriendsOfCake')
- ),
- 1 => array(
- 'User' => array('id' => 45, 'name' => 'CakePHP'),
- 'Profile' => array('id' => 123, 'twitter' => '@cakephp')
- )
- );
-
- $controller->viewVars = compact('success', 'users');
-
- $action
- ->expects($this->once())
- ->method('viewVar')
- ->will($this->returnValue('users'));
-
- $model->alias = 'User';
-
- $listener
- ->expects($this->never())
- ->method('_changeNesting');
-
- $listener
- ->expects($this->at($i++))
- ->method('_recurse')
- ->with($this->identicalTo($users[0]));
-
- $listener
- ->expects($this->at($i++))
- ->method('_recurse')
- ->with($this->identicalTo($users[1]));
-
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
-
- $controller
- ->expects($this->once())
- ->method('set')
- ->with('users', $users);
-
- $this->assertTrue($listener->beforeRender());
- }
-
-/**
- * testBeforeRenderWithFindFirstAndNestingChange
- *
- * @return void
- */
- public function testBeforeRenderWithFindFirstAndNestingChange() {
- $i = 0;
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->setMethods(array('_request', '_controller', '_action', '_model', '_changeNesting', '_recurse', '_setMethods'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request = $this
- ->getMockBuilder('CakeRequest')
- ->setMethods(array('is'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request
- ->expects($this->once())
- ->method('is')
- ->with('api')
- ->will($this->returnValue(true));
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('set'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $action = $this
- ->getMockBuilder('IndexAction')
- ->setMethods(array('viewVar'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $model = $this
- ->getMockBuilder('Model')
- ->disableOriginalConstructor()
- ->getMock();
-
- $listener
- ->expects($this->at($i++))
- ->method('_request')
- ->will($this->returnValue($request));
-
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
-
- $listener
- ->expects($this->at($i++))
- ->method('_action')
- ->will($this->returnValue($action));
-
- $listener
- ->expects($this->at($i++))
- ->method('_setMethods');
-
- $listener
- ->expects($this->at($i++))
- ->method('_model')
- ->will($this->returnValue($model));
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => true,
- 'changeTime' => true,
- 'castNumbers' => true,
- 'keyMethods' => array(),
- 'valueMethods' => array(),
- 'replaceMap' => array()
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $success = true;
- $user = array(
- 'User' => array(
- 'id' => 5,
- 'name' => 'FriendsOfCake'
- ),
- 'Profile' => array(
- 'id' => 987,
- 'twitter' => '@FriendsOfCake'
- )
- );
-
- $controller->viewVars = compact('success', 'user');
-
- $action
- ->expects($this->once())
- ->method('viewVar')
- ->will($this->returnValue('user'));
-
- $model->alias = 'User';
-
- $nested = array(
- 'id' => 5,
- 'name' => 'FriendsOfCake',
- 'Profile' => array('id' => 987, 'twitter' => '@FriendsOfCake')
- );
-
- $listener
- ->expects($this->at($i++))
- ->method('_changeNesting')
- ->with($this->identicalTo($user), 'User')
- ->will($this->returnValue($nested));
-
- $listener
- ->expects($this->at($i++))
- ->method('_recurse')
- ->with($nested);
-
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
-
- $controller
- ->expects($this->once())
- ->method('set')
- ->with('user', $nested);
-
- $this->assertTrue($listener->beforeRender());
- }
-
-/**
- * testBeforeRenderWithoutViewVar
- *
- * @return void
- */
- public function testBeforeRenderWithoutViewVar() {
- $i = 0;
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->setMethods(array('_request', '_controller', '_action', '_model', '_changeNesting', '_recurse', '_setMethods'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request = $this
- ->getMockBuilder('CakeRequest')
- ->setMethods(array('is'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request
- ->expects($this->once())
- ->method('is')
- ->with('api')
- ->will($this->returnValue(true));
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('set'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $action = $this
- ->getMockBuilder('IndexAction')
- ->setMethods(array('viewVar'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $model = $this
- ->getMockBuilder('Model')
- ->disableOriginalConstructor()
- ->getMock();
-
- $listener
- ->expects($this->at($i++))
- ->method('_request')
- ->will($this->returnValue($request));
-
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
-
- $listener
- ->expects($this->at($i++))
- ->method('_action')
- ->will($this->returnValue($action));
-
- $listener
- ->expects($this->never())
- ->method('_model')
- ->will($this->returnValue($model));
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => true,
- 'changeTime' => true,
- 'castNumbers' => true,
- 'keyMethods' => array(),
- 'valueMethods' => array(),
- 'replaceMap' => array()
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $success = true;
-
- $controller->viewVars = compact('success');
-
- $action
- ->expects($this->once())
- ->method('viewVar')
- ->will($this->returnValue('users'));
-
- $model->alias = 'User';
-
- $listener
- ->expects($this->never())
- ->method('_changeNesting');
-
- $listener
- ->expects($this->never())
- ->method('_recurse');
-
- $controller
- ->expects($this->never())
- ->method('set');
-
- $this->assertTrue($listener->beforeRender());
- }
-
-/**
- * testBeforeRenderWithEmptyViewVar
- *
- * @return void
- */
- public function testBeforeRenderWithEmptyViewVar() {
- $i = 0;
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->setMethods(array('_request', '_controller', '_action', '_model', '_changeNesting', '_recurse', '_setMethods'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request = $this
- ->getMockBuilder('CakeRequest')
- ->setMethods(array('is'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $request
- ->expects($this->once())
- ->method('is')
- ->with('api')
- ->will($this->returnValue(true));
-
- $controller = $this
- ->getMockBuilder('Controller')
- ->setMethods(array('set'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $action = $this
- ->getMockBuilder('IndexAction')
- ->setMethods(array('viewVar'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $model = $this
- ->getMockBuilder('Model')
- ->disableOriginalConstructor()
- ->getMock();
-
- $listener
- ->expects($this->at($i++))
- ->method('_request')
- ->will($this->returnValue($request));
-
- $listener
- ->expects($this->at($i++))
- ->method('_controller')
- ->will($this->returnValue($controller));
-
- $listener
- ->expects($this->at($i++))
- ->method('_action')
- ->will($this->returnValue($action));
-
- $listener
- ->expects($this->never())
- ->method('_model')
- ->will($this->returnValue($model));
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => true,
- 'changeTime' => true,
- 'castNumbers' => true,
- 'keyMethods' => array(),
- 'valueMethods' => array(),
- 'replaceMap' => array()
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $success = true;
- $users = array();
-
- $controller->viewVars = compact('success', 'users');
-
- $action
- ->expects($this->once())
- ->method('viewVar')
- ->will($this->returnValue('users'));
-
- $model->alias = 'User';
-
- $listener
- ->expects($this->never())
- ->method('_changeNesting');
-
- $listener
- ->expects($this->never())
- ->method('_recurse');
-
- $controller
- ->expects($this->never())
- ->method('set');
-
- $this->assertTrue($listener->beforeRender());
- }
-
-/**
- * testSetMethods
- *
- * @return void
- */
- public function testSetMethods() {
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->setReflectionClassInstance($listener);
-
- $closure = function($variable) {
- return $variable;
- };
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => true,
- 'changeTime' => true,
- 'castNumbers' => true,
- 'keyMethods' => array(),
- 'valueMethods' => array($closure),
- 'replaceMap' => array()
- );
-
- $this->setProtectedProperty('_settings', $settings, $listener);
- $this->callProtectedMethod('_setMethods', array(), $listener);
- $result = $this->getProtectedProperty('_settings', $listener);
-
- $expected = array('_replaceKeys');
- $this->assertSame($expected, $result['keyMethods']);
-
- $expected = array('_castNumbers', '_changeDateToUnix', $closure);
- $this->assertSame($expected, $result['valueMethods']);
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => false,
- 'changeTime' => false,
- 'castNumbers' => true,
- 'keyMethods' => array(),
- 'valueMethods' => array($closure),
- 'replaceMap' => array()
- );
-
- $this->setProtectedProperty('_settings', $settings, $listener);
- $this->callProtectedMethod('_setMethods', array(), $listener);
- $result = $this->getProtectedProperty('_settings', $listener);
-
- $expected = array();
- $this->assertSame($expected, $result['keyMethods']);
-
- $expected = array('_castNumbers', $closure);
- $this->assertSame($expected, $result['valueMethods']);
- }
-
-/**
- * testRecurseWithKeysAndCasts
- *
- * @return void
- */
- public function testRecurseWithKeysAndCasts() {
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->disableOriginalConstructor()
- ->getMock();
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => true,
- 'changeTime' => true,
- 'castNumbers' => true,
- 'keyMethods' => array('_replaceKeys'),
- 'valueMethods' => array('_castNumbers', '_changeDateToUnix'),
- 'replaceMap' => array('User' => 'user', 'Profile' => 'profile')
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $data = array(
- 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
- 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
- );
-
- $expected = array(
- 'user' => array('id' => 5, 'name' => 'FriendsOfCake'),
- 'profile' => array('id' => 987, 'twitter' => '@FriendsOfCake')
- );
-
- $this->callProtectedMethod('_recurse', array(&$data), $listener);
-
- $this->assertSame($expected, $data);
- }
-
-/**
- * testRecurseNoCasts
- *
- * @return void
- */
- public function testRecurseNoCasts() {
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->disableOriginalConstructor()
- ->getMock();
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => true,
- 'changeTime' => false,
- 'castNumbers' => false,
- 'keyMethods' => array('_replaceKeys'),
- 'valueMethods' => array(),
- 'replaceMap' => array('User' => 'user', 'Profile' => 'profile')
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $data = array(
- 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
- 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
- );
-
- $expected = array(
- 'user' => array('id' => '5', 'name' => 'FriendsOfCake'),
- 'profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
- );
-
- $this->callProtectedMethod('_recurse', array(&$data), $listener);
-
- $this->assertSame($expected, $data);
- }
-
-/**
- * testRecurseNoCastsMapWithoutMatch
- *
- * @return void
- */
- public function testRecurseNoCastsMapWithoutMatch() {
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->disableOriginalConstructor()
- ->getMock();
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => true,
- 'changeTime' => false,
- 'castNumbers' => false,
- 'keyMethods' => array('_replaceKeys'),
- 'valueMethods' => array(),
- 'replaceMap' => array('CakePHP' => 'FriendsOfCake')
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $data = array(
- 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
- 'Comment' => array()
- );
-
- $expected = array(
- 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
- 'Comment' => array()
- );
-
- $this->callProtectedMethod('_recurse', array(&$data), $listener);
-
- $this->assertSame($expected, $data);
- }
-
-/**
- * testRecurseNoCastsHasMany
- *
- * @return void
- */
- public function testRecurseNoCastsEmptyHasMany() {
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->setMethods(array('_model'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $user = $this
- ->getMockBuilder('Model')
- ->disableOriginalConstructor()
- ->setMethods(array('find'))
- ->getMock();
- $user->name = $user->alias = 'User';
-
- $comment = $this
- ->getMockBuilder('Model')
- ->disableOriginalConstructor()
- ->setMethods(array('find'))
- ->getMock();
- $comment->name = $comment->alias = 'Comment';
-
- $user->hasMany = array('Comment' => array('className' => 'Comment', 'foreign_key' => 'user_id'));
- $user->Comment = $comment;
-
- $listener
- ->expects($this->once())
- ->method('_model')
- ->will($this->returnValue($user));
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => true,
- 'changeTime' => false,
- 'castNumbers' => false,
- 'keyMethods' => array('_replaceKeys'),
- 'valueMethods' => array(),
- 'replaceMap' => array()
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $data = array(
- 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
- 'Comment' => array()
- );
-
- $expected = array(
- 'user' => array('id' => '5', 'name' => 'FriendsOfCake'),
- 'comments' => array()
- );
-
- $this->callProtectedMethod('_recurse', array(&$data), $listener);
-
- $this->assertSame($expected, $data);
- }
-
-/**
- * testRecurseNoKeys
- *
- * @return void
- */
- public function testRecurseNoKeys() {
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->disableOriginalConstructor()
- ->getMock();
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => false,
- 'changeTime' => true,
- 'castNumbers' => true,
- 'keyMethods' => array(),
- 'valueMethods' => array('_castNumbers', '_changeDateToUnix'),
- 'replaceMap' => array()
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $data = array(
- 'User' => array('id' => '5', 'name' => 'FriendsOfCake', 'created' => '2013-08-26 11:24:54'),
- 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
- );
-
- $expected = array(
- 'User' => array(
- 'id' => 5,
- 'name' => 'FriendsOfCake',
- 'created' => strtotime('2013-08-26 11:24:54')
- ),
- 'Profile' => array(
- 'id' => 987,
- 'twitter' => '@FriendsOfCake'
- )
- );
-
- $this->callProtectedMethod('_recurse', array(&$data), $listener);
-
- $this->assertSame($expected, $data);
- }
-
-/**
- * testRecurseNoKeysAndNoCasts
- *
- * @return void
- */
- public function testRecurseNoKeysAndNoCasts() {
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->disableOriginalConstructor()
- ->getMock();
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => false,
- 'changeTime' => false,
- 'castNumbers' => false,
- 'keyMethods' => array(),
- 'valueMethods' => array(),
- 'replaceMap' => array()
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $data = array(
- 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
- 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
- );
-
- $expected = $data;
-
- $this->callProtectedMethod('_recurse', array(&$data), $listener);
-
- $this->assertSame($expected, $data);
- }
-
-/**
- * testRecurseWithGlobalFunction
- *
- * @return void
- */
- public function testRecurseWithGlobalFunction() {
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->disableOriginalConstructor()
- ->getMock();
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => false,
- 'changeTime' => false,
- 'castNumbers' => false,
- 'keyMethods' => array(),
- 'valueMethods' => array(function($value, $key) {
- return strtoupper($value);
- }),
- 'replaceMap' => array()
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $data = array(
- 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
- 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
- );
-
- $expected = array(
- 'User' => array('id' => '5', 'name' => 'FRIENDSOFCAKE'),
- 'Profile' => array('id' => '987', 'twitter' => '@FRIENDSOFCAKE')
- );
-
- $this->callProtectedMethod('_recurse', array(&$data), $listener);
-
- $this->assertSame($expected, $data);
- }
-
-/**
- * testRecurseWithStaticMethod
- *
- * @return void
- */
- public function testRecurseWithStaticMethod() {
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->disableOriginalConstructor()
- ->getMock();
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => false,
- 'changeTime' => false,
- 'castNumbers' => false,
- 'keyMethods' => array(),
- 'valueMethods' => array('ApiTransformationListenerTest::staticExample'),
- 'replaceMap' => array()
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $data = array(
- 'User' => array('id' => '5', 'name' => 'Friends Of Cake'),
- 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
- );
-
- $expected = array(
- 'User' => array('id' => '5', 'name' => 'Friends_Of_Cake'),
- 'Profile' => array('id' => '987', 'twitter' => 'FriendsOfCake')
- );
-
- $this->callProtectedMethod('_recurse', array(&$data), $listener);
-
- $this->assertSame($expected, $data);
- }
-
-/**
- * testRecurseWithClosure
- *
- * @return void
- */
- public function testRecurseWithClosure() {
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->disableOriginalConstructor()
- ->getMock();
-
- $uppercase = function($variable) {
- if (!is_string($variable)) {
- return $variable;
- }
- return strtoupper($variable);
- };
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => false,
- 'changeTime' => false,
- 'castNumbers' => false,
- 'keyMethods' => array(),
- 'valueMethods' => array($uppercase),
- 'replaceMap' => array()
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $data = array(
- 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
- 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
- );
-
- $expected = array(
- 'User' => array('id' => '5', 'name' => 'FRIENDSOFCAKE'),
- 'Profile' => array('id' => '987', 'twitter' => '@FRIENDSOFCAKE')
- );
-
- $this->callProtectedMethod('_recurse', array(&$data), $listener);
-
- $this->assertSame($expected, $data);
- }
-
- public function testRecurseKeySpecificTransform() {
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->disableOriginalConstructor()
- ->getMock();
-
- $callback = function($variable, $key) {
- if ($key === 'User.changeme') {
- return 'changed';
- }
- return $variable;
- };
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => false,
- 'changeTime' => false,
- 'castNumbers' => false,
- 'keyMethods' => array(),
- 'valueMethods' => array($callback),
- 'replaceMap' => array()
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $data = array(
- 'User' => array(
- 'id' => '5',
- 'name' => 'FriendsOfCake',
- 'changeme' => 'old'
- ),
- 'Profile' => array(
- 'id' => '987',
- 'twitter' => '@FriendsOfCake'
- )
- );
-
- $expected = array(
- 'User' => array(
- 'id' => '5',
- 'name' => 'FriendsOfCake',
- 'changeme' => 'changed'
- ),
- 'Profile' => array(
- 'id' => '987',
- 'twitter' => '@FriendsOfCake'
- )
- );
-
- $this->callProtectedMethod('_recurse', array(&$data), $listener);
-
- $this->assertSame($expected, $data);
- }
-
- public function testRecurseKeySpecificNestedTransform() {
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->disableOriginalConstructor()
- ->getMock();
-
- $callback = function($variable, $key) {
- if (preg_match('@^\d+.User\.changeme$@', $key)) {
- return 'changed';
- }
- return $variable;
- };
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => false,
- 'changeTime' => false,
- 'castNumbers' => false,
- 'keyMethods' => array(),
- 'valueMethods' => array($callback),
- 'replaceMap' => array()
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $data = array(
- 'changeme' => 'no change',
- array(
- 'changeme' => 'no change',
- 'User' => array(
- 'id' => '5',
- 'name' => 'FriendsOfCake',
- 'changeme' => 'old'
- ),
- 'Profile' => array(
- 'id' => '987',
- 'twitter' => '@FriendsOfCake'
- )
- ),
- array(
- 'User' => array(
- 'id' => '6',
- 'name' => 'FriendsOfCake',
- 'changeme' => 'old two'
- ),
- 'Profile' => array(
- 'id' => '987',
- 'twitter' => '@FriendsOfCake'
- )
- )
- );
-
- $expected = array(
- 'changeme' => 'no change',
- array(
- 'changeme' => 'no change',
- 'User' => array(
- 'id' => '5',
- 'name' => 'FriendsOfCake',
- 'changeme' => 'changed'
- ),
- 'Profile' => array(
- 'id' => '987',
- 'twitter' => '@FriendsOfCake'
- )
- ),
- array(
- 'User' => array(
- 'id' => '6',
- 'name' => 'FriendsOfCake',
- 'changeme' => 'changed'
- ),
- 'Profile' => array(
- 'id' => '987',
- 'twitter' => '@FriendsOfCake'
- )
- )
- );
-
- $this->callProtectedMethod('_recurse', array(&$data), $listener);
-
- $this->assertSame($expected, $data);
- }
-
-/**
- * testGetReplaceMapFromAssociationsEndlessLoopPrevention
- *
- * @return void
- */
- public function testGetReplaceMapFromAssociationsEndlessLoopPrevention() {
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->setMethods(array('_model'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $user = $this
- ->getMockBuilder('Model')
- ->disableOriginalConstructor()
- ->setMethods(array('find'))
- ->getMock();
- $user->name = $user->alias = 'User';
-
- $user->belongsTo = array('User' => array('className' => 'User', 'foreignKey' => 'user_id'));
- $user->User = $user;
-
- $listener
- ->expects($this->once())
- ->method('_model')
- ->will($this->returnValue($user));
-
- $this->setReflectionClassInstance($listener);
-
- $expected = array('User' => 'user');
- $result = $this->callProtectedMethod('_getReplaceMapFromAssociations', array(), $listener);
-
- $this->assertSame($expected, $result);
- }
-
-/**
- * testGetReplaceMapFromAssociationsDeepSingleRecordAssociations
- *
- * @return void
- */
- public function testGetReplaceMapFromAssociationsDeepSingleRecordAssociations() {
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->setMethods(array('_model'))
- ->disableOriginalConstructor()
- ->getMock();
-
- $user = $this
- ->getMockBuilder('Model')
- ->disableOriginalConstructor()
- ->setMethods(array('find'))
- ->getMock();
- $user->name = $user->alias = 'User';
-
- $group = $this
- ->getMockBuilder('Model')
- ->disableOriginalConstructor()
- ->setMethods(array('find'))
- ->getMock();
- $group->name = $group->alias = 'Group';
-
- $ambassador = $this
- ->getMockBuilder('Model')
- ->disableOriginalConstructor()
- ->setMethods(array('find'))
- ->getMock();
- $ambassador->name = $ambassador->alias = 'Ambassador';
-
- $user->belongsTo = array('Group' => array('className' => 'Group', 'foreignKey' => 'group_id'));
- $group->hasOne = array('Ambassador' => array('className' => 'Ambassador', 'foreignKey' => 'group_id'));
-
- $group->Ambassador = $ambassador;
- $user->Group = $group;
-
- $listener
- ->expects($this->once())
- ->method('_model')
- ->will($this->returnValue($user));
-
- $this->setReflectionClassInstance($listener);
-
- $expected = array('User' => 'user', 'Group' => 'group', 'Ambassador' => 'ambassador');
- $result = $this->callProtectedMethod('_getReplaceMapFromAssociations', array(), $listener);
-
- $this->assertSame($expected, $result);
- }
-
-/**
- * testChangeNesting
- *
- * @return void
- */
- public function testChangeNesting() {
- $listener = $this
- ->getMockBuilder('ApiTransformationListener')
- ->disableOriginalConstructor()
- ->getMock();
-
- $settings = array(
- 'changeNesting' => true,
- 'changeKeys' => false,
- 'changeTime' => false,
- 'castNumbers' => false,
- 'keyMethods' => array(),
- 'valueMethods' => array(),
- 'replaceMap' => array()
- );
-
- $this->setReflectionClassInstance($listener);
- $this->setProtectedProperty('_settings', $settings, $listener);
-
- $data = array(
- 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
- 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
- );
-
- $expected = array(
- 'id' => '5',
- 'name' => 'FriendsOfCake',
- 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
- );
-
- $result = $this->callProtectedMethod('_changeNesting', array(&$data, 'User'), $listener);
-
- $this->assertSame($expected, $result);
-
- $data = array(
- 'User' => array('id' => '5', 'name' => 'FriendsOfCake'),
- 'Profile' => array('id' => '987', 'twitter' => '@FriendsOfCake')
- );
-
- $expected = array(
- 'id' => '987',
- 'twitter' => '@FriendsOfCake',
- 'User' => array(
- 'id' => '5',
- 'name' => 'FriendsOfCake',
- )
- );
-
- $result = $this->callProtectedMethod('_changeNesting', array(&$data, 'Profile'), $listener);
-
- $this->assertSame($expected, $result);
- }
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/RedirectListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/RedirectListenerTest.php
deleted file mode 100644
index c25e213ca..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/RedirectListenerTest.php
+++ /dev/null
@@ -1,529 +0,0 @@
-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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/RelatedModelsListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/RelatedModelsListenerTest.php
deleted file mode 100644
index cbff0ffd2..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/RelatedModelsListenerTest.php
+++ /dev/null
@@ -1,826 +0,0 @@
-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();
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ScaffoldListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ScaffoldListenerTest.php
deleted file mode 100644
index 6ef834438..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/ScaffoldListenerTest.php
+++ /dev/null
@@ -1,323 +0,0 @@
- '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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/SearchListenerTest.php b/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/SearchListenerTest.php
deleted file mode 100644
index bbf5cd07f..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Controller/Crud/Listener/SearchListenerTest.php
+++ /dev/null
@@ -1,719 +0,0 @@
-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');
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Error/CrudExceptionRendererTest.php b/web/api/app/Plugin/Crud/Test/Case/Error/CrudExceptionRendererTest.php
deleted file mode 100644
index 021fd1310..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Error/CrudExceptionRendererTest.php
+++ /dev/null
@@ -1,528 +0,0 @@
-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']);
- }
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Lib/Panel/CrudPanelTest.php b/web/api/app/Plugin/Crud/Test/Case/Lib/Panel/CrudPanelTest.php
deleted file mode 100644
index c3aa1bbe0..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Lib/Panel/CrudPanelTest.php
+++ /dev/null
@@ -1,89 +0,0 @@
-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');
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Routing/Filter/FakeHeadFilterTest.php b/web/api/app/Plugin/Crud/Test/Case/Routing/Filter/FakeHeadFilterTest.php
deleted file mode 100644
index e5e328b23..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Routing/Filter/FakeHeadFilterTest.php
+++ /dev/null
@@ -1,51 +0,0 @@
-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');
- }
-}
diff --git a/web/api/app/Plugin/Crud/Test/Case/Routing/Filter/HttpMethodFilterTest.php b/web/api/app/Plugin/Crud/Test/Case/Routing/Filter/HttpMethodFilterTest.php
deleted file mode 100644
index 72ed559b6..000000000
--- a/web/api/app/Plugin/Crud/Test/Case/Routing/Filter/HttpMethodFilterTest.php
+++ /dev/null
@@ -1,207 +0,0 @@
-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');
- }
-}
diff --git a/web/api/app/Plugin/Crud/Test/Fixture/PostsTagFixture.php b/web/api/app/Plugin/Crud/Test/Fixture/PostsTagFixture.php
deleted file mode 100644
index 1c112725e..000000000
--- a/web/api/app/Plugin/Crud/Test/Fixture/PostsTagFixture.php
+++ /dev/null
@@ -1,35 +0,0 @@
- 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(
-
- );
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Support/CrudControllerTestCase.php b/web/api/app/Plugin/Crud/Test/Support/CrudControllerTestCase.php
deleted file mode 100644
index d2f949fd1..000000000
--- a/web/api/app/Plugin/Crud/Test/Support/CrudControllerTestCase.php
+++ /dev/null
@@ -1,169 +0,0 @@
- 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;
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/Test/Support/CrudTestCase.php b/web/api/app/Plugin/Crud/Test/Support/CrudTestCase.php
deleted file mode 100644
index 9dc02eee1..000000000
--- a/web/api/app/Plugin/Crud/Test/Support/CrudTestCase.php
+++ /dev/null
@@ -1,168 +0,0 @@
- 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;
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/View/CrudJsonView.php b/web/api/app/Plugin/Crud/View/CrudJsonView.php
deleted file mode 100644
index 03a8ffdd2..000000000
--- a/web/api/app/Plugin/Crud/View/CrudJsonView.php
+++ /dev/null
@@ -1,48 +0,0 @@
- $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);
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/View/CrudXmlView.php b/web/api/app/Plugin/Crud/View/CrudXmlView.php
deleted file mode 100644
index 75b6d934a..000000000
--- a/web/api/app/Plugin/Crud/View/CrudXmlView.php
+++ /dev/null
@@ -1,52 +0,0 @@
-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();
- }
-
-}
diff --git a/web/api/app/Plugin/Crud/View/Elements/crud_panel.ctp b/web/api/app/Plugin/Crud/View/Elements/crud_panel.ctp
deleted file mode 100644
index 501f47a42..000000000
--- a/web/api/app/Plugin/Crud/View/Elements/crud_panel.ctp
+++ /dev/null
@@ -1,17 +0,0 @@
-= __d('crud', 'Config'); ?>
- $crudDebugKitData['action'],
- __d('crud', 'Component') => $crudDebugKitData['component'],
- __d('crud', 'Listeners') => $crudDebugKitData['listeners']
-);
-
-echo $this->Toolbar->makeNeatArray($config);
-?>
-= __d('crud', 'Events triggered'); ?>
-Toolbar->makeNeatArray($crudDebugKitData['events']);
diff --git a/web/api/app/Plugin/Crud/composer.json b/web/api/app/Plugin/Crud/composer.json
deleted file mode 100644
index 679570e98..000000000
--- a/web/api/app/Plugin/Crud/composer.json
+++ /dev/null
@@ -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"
- }
-}