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

+ $crudDebugKitData['action'], + __d('crud', 'Component') => $crudDebugKitData['component'], + __d('crud', 'Listeners') => $crudDebugKitData['listeners'] +); + +echo $this->Toolbar->makeNeatArray($config); +?> +

+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 @@ + + + + + <?php echo $title_for_layout; ?> + + + 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(); ?> + + <?php echo $cakeDescription ?>: + <?php echo $title_for_layout; ?> + + 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(); ?> + + <?php echo $cakeDescription ?>: + <?php echo $title_for_layout; ?> + + 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(); ?> +<?php echo $page_title; ?> + + + + + + + +

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

- $crudDebugKitData['action'], - __d('crud', 'Component') => $crudDebugKitData['component'], - __d('crud', 'Listeners') => $crudDebugKitData['listeners'] -); - -echo $this->Toolbar->makeNeatArray($config); -?> -

-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" - } -}