├── LICENSE
├── README.md
├── composer.json
├── config
├── app.default.php
└── bootstrap.php
├── docs
├── Component
│ └── Setup.md
├── Console
│ ├── Bake.md
│ └── Commands.md
├── Controller
│ └── Setup.md
├── Healthcheck
│ └── Healthcheck.md
├── Install.md
├── Maintenance
│ ├── Maintenance.md
│ └── Uptime.md
├── Panel
│ └── L10nPanel.md
└── README.md
├── phpcs.xml
├── phpstan.neon
├── src
├── Auth
│ ├── AbstractPasswordHasher.php
│ ├── DefaultPasswordHasher.php
│ └── PasswordHasherFactory.php
├── Command
│ ├── CliTestCommand.php
│ ├── CurrentConfigConfigureCommand.php
│ ├── CurrentConfigDisplayCommand.php
│ ├── CurrentConfigPhpinfoCommand.php
│ ├── CurrentConfigValidateCommand.php
│ ├── DbBackupCreateCommand.php
│ ├── DbBackupRestoreCommand.php
│ ├── DbInitCommand.php
│ ├── DbIntegrityBoolsCommand.php
│ ├── DbIntegrityConstraintsCommand.php
│ ├── DbIntegrityIntsCommand.php
│ ├── DbIntegrityKeysCommand.php
│ ├── DbIntegrityNullsCommand.php
│ ├── DbResetCommand.php
│ ├── DbWipeCommand.php
│ ├── HealthcheckCommand.php
│ ├── MailCheckCommand.php
│ ├── MaintenanceModeActivateCommand.php
│ ├── MaintenanceModeDeactivateCommand.php
│ ├── MaintenanceModeStatusCommand.php
│ ├── MaintenanceModeWhitelistCommand.php
│ ├── ResetCommand.php
│ ├── Traits
│ │ ├── DbBackupTrait.php
│ │ └── DbToolsTrait.php
│ ├── UserCreateCommand.php
│ └── UserUpdateCommand.php
├── Controller
│ ├── Admin
│ │ ├── BackendController.php
│ │ ├── ConfigurationController.php
│ │ ├── DatabaseController.php
│ │ ├── SetupController.php
│ │ └── UptimeController.php
│ ├── Component
│ │ └── SetupComponent.php
│ └── HealthcheckController.php
├── Healthcheck
│ ├── Check
│ │ ├── Check.php
│ │ ├── CheckInterface.php
│ │ ├── Core
│ │ │ ├── CakeCacheCheck.php
│ │ │ ├── CakeSaltCheck.php
│ │ │ ├── CakeVersionCheck.php
│ │ │ └── FullBaseUrlCheck.php
│ │ ├── Database
│ │ │ └── ConnectCheck.php
│ │ └── Environment
│ │ │ ├── PhpUploadLimitCheck.php
│ │ │ └── PhpVersionCheck.php
│ ├── Healthcheck.php
│ └── HealthcheckCollector.php
├── Maintenance
│ └── Maintenance.php
├── Middleware
│ └── MaintenanceMiddleware.php
├── Panel
│ └── L10nPanel.php
├── Queue
│ └── Task
│ │ └── HealthcheckTask.php
├── SetupPlugin.php
├── TestSuite
│ └── DriverSkipTrait.php
├── Utility
│ ├── ClassFinder.php
│ ├── Config.php
│ ├── Debug.php
│ ├── OrmTypes.php
│ ├── Setup.php
│ ├── System.php
│ └── Validation.php
└── View
│ └── Helper
│ └── SetupBakeHelper.php
├── templates
├── Admin
│ ├── Backend
│ │ ├── cache.php
│ │ ├── cookies.php
│ │ ├── database.php
│ │ ├── disk_space.php
│ │ ├── env.php
│ │ ├── ip.php
│ │ ├── locales.php
│ │ ├── phpinfo.php
│ │ ├── session.php
│ │ ├── system.php
│ │ ├── timezones.php
│ │ └── type_map.php
│ ├── Configuration
│ │ └── index.php
│ ├── Database
│ │ └── foreign_keys.php
│ └── Setup
│ │ ├── index.php
│ │ └── maintenance.php
├── Healthcheck
│ └── index.php
├── bake
│ ├── Command
│ │ ├── command.twig
│ │ └── helper.twig
│ ├── Controller
│ │ ├── component.twig
│ │ └── controller.twig
│ ├── Form
│ │ └── form.twig
│ ├── Mailer
│ │ └── mailer.twig
│ ├── Middleware
│ │ └── middleware.twig
│ ├── Model
│ │ ├── behavior.twig
│ │ ├── entity.twig
│ │ ├── enum.twig
│ │ └── table.twig
│ ├── Plugin
│ │ ├── .editorconfig.twig
│ │ ├── .gitattributes.twig
│ │ ├── .gitignore.twig
│ │ ├── README.md.twig
│ │ ├── composer.json.twig
│ │ ├── phpunit.xml.dist.twig
│ │ ├── src
│ │ │ ├── Controller
│ │ │ │ └── AppController.php.twig
│ │ │ └── Plugin.php.twig
│ │ ├── tests
│ │ │ └── bootstrap.php.twig
│ │ └── webroot
│ │ │ └── .gitkeep.twig
│ ├── Template
│ │ ├── add.twig
│ │ ├── edit.twig
│ │ ├── index.twig
│ │ ├── login.twig
│ │ └── view.twig
│ ├── View
│ │ ├── cell.twig
│ │ └── helper.twig
│ ├── element
│ │ ├── Controller
│ │ │ ├── add.twig
│ │ │ ├── delete.twig
│ │ │ ├── edit.twig
│ │ │ ├── index.twig
│ │ │ ├── login.twig
│ │ │ ├── logout.twig
│ │ │ └── view.twig
│ │ ├── array_property.twig
│ │ ├── file_header.twig
│ │ └── form.twig
│ ├── layout
│ │ └── default.twig
│ └── tests
│ │ ├── fixture.twig
│ │ └── test_case.twig
└── element
│ ├── l10n_panel.php
│ ├── ok.php
│ └── yes_no.php
└── tests
├── Fixture
├── QueuedJobsFixture.php
├── SessionsFixture.php
└── UsersFixture.php
├── TestCase
├── Command
│ ├── CliTestCommandTest.php
│ ├── CurrentConfigCommandTest.php
│ ├── DbInitCommandTest.php
│ ├── DbResetCommandTest.php
│ ├── DbWipeCommandTest.php
│ ├── HealthcheckCommandTest.php
│ ├── MaintenanceModeActivateCommandTest.php
│ ├── MaintenanceModeDeactivateCommandTest.php
│ ├── MaintenanceModeStatusCommandTest.php
│ ├── MaintenanceModeWhitelistCommandTest.php
│ ├── ResetCommandTest.php
│ ├── UserCreateCommandTest.php
│ └── UserUpdateCommandTest.php
├── Controller
│ ├── Admin
│ │ ├── BackendControllerTest.php
│ │ ├── ConfigurationControllerTest.php
│ │ ├── DatabaseControllerTest.php
│ │ └── SetupControllerTest.php
│ ├── Component
│ │ └── SetupComponentTest.php
│ └── HealthcheckControllerTest.php
├── Healthcheck
│ └── Check
│ │ ├── Core
│ │ ├── CakeCacheCheckTest.php
│ │ ├── CakeSaltCheckTest.php
│ │ ├── CakeVersionCheckTest.php
│ │ └── FullBaseUrlCheckTest.php
│ │ ├── Database
│ │ └── ConnectCheckTest.php
│ │ ├── Environment
│ │ ├── PhpUploadLimitCheckTest.php
│ │ └── PhpVersionCheckTest.php
│ │ └── HealthcheckCollectorTest.php
├── Maintenance
│ └── MaintenanceTest.php
├── Middleware
│ └── MaintenanceMiddlewareTest.php
├── Queue
│ └── Task
│ │ └── HealthcheckTaskTest.php
└── Utility
│ └── SetupTest.php
├── config
├── bootstrap.php
└── routes.php
├── schema.php
└── shim.php
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Mark Scherer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CakePHP Setup Plugin
2 |
3 | [](https://github.com/dereuromark/cakephp-setup/actions/workflows/ci.yml?query=branch%3Amaster)
4 | [](https://codecov.io/gh/dereuromark/cakephp-setup)
5 | [](https://packagist.org/packages/dereuromark/cakephp-setup)
6 | [](https://php.net/)
7 | [](LICENSE)
8 | [](https://packagist.org/packages/dereuromark/cakephp-setup)
9 | [](https://github.com/php-fig-rectified/fig-rectified-standards)
10 |
11 | Provides useful development tools for managing a CakePHP app.
12 |
13 | Note: This branch is for **CakePHP 5.1+**. See [version map](https://github.com/dereuromark/cakephp-setup/wiki#cakephp-version-map) for details.
14 |
15 | ## What is this plugin for?
16 | This plugin aims to be a help for development of CakePHP applications. It extends and leverages
17 | my Tools Plugin.
18 |
19 | Currently, this plugin contains only the parts I managed to migrate yet:
20 |
21 | * Maintenance Mode (dynamic activation and deactivation incl. dynamic IP whitelisting)
22 | * Some very useful development tools and debugging shells
23 |
24 | ## Installation & Docs
25 |
26 | - [Documentation](docs/README.md)
27 |
28 | Possible dependencies: [Tools](https://github.com/dereuromark/cakephp-tools) Plugin
29 |
30 | ## Disclaimer
31 | Use at your own risk. Please provide any fixes or enhancements via issue or better pull request.
32 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dereuromark/cakephp-setup",
3 | "description": "A CakePHP plugin containing lots of useful management tools",
4 | "license": "MIT",
5 | "type": "cakephp-plugin",
6 | "keywords": [
7 | "cakephp",
8 | "plugin",
9 | "setup",
10 | "management",
11 | "maintenance",
12 | "backup",
13 | "bake",
14 | "templates"
15 | ],
16 | "authors": [
17 | {
18 | "name": "Mark Scherer",
19 | "homepage": "https://www.dereuromark.de",
20 | "role": "Author"
21 | }
22 | ],
23 | "homepage": "https://github.com/dereuromark/cakephp-setup",
24 | "support": {
25 | "issues": "https://github.com/dereuromark/cakephp-setup/issues",
26 | "source": "https://github.com/dereuromark/cakephp-setup"
27 | },
28 | "require": {
29 | "php": ">=8.1",
30 | "cakephp/cakephp": "^5.1.1"
31 | },
32 | "require-dev": {
33 | "cakephp/bake": "^3.0.1",
34 | "cakephp/debug_kit": "^5.0.1",
35 | "cakephp/migrations": "^4.5.1",
36 | "dereuromark/cakephp-tools": "^3.0.0",
37 | "dereuromark/cakephp-queue": "^8.2.0",
38 | "dereuromark/cakephp-templating": "^0.2.5",
39 | "fig-r/psr2r-sniffer": "dev-master",
40 | "phpunit/phpunit": "^10.5 || ^11.5 || ^12.1",
41 | "composer/semver": "^3.4"
42 | },
43 | "minimum-stability": "stable",
44 | "prefer-stable": true,
45 | "autoload": {
46 | "psr-4": {
47 | "Setup\\": "src/"
48 | }
49 | },
50 | "autoload-dev": {
51 | "psr-4": {
52 | "Cake\\Test\\": "vendor/cakephp/cakephp/tests/",
53 | "Setup\\Test\\": "tests/",
54 | "TestApp\\": "tests/test_app/src/"
55 | }
56 | },
57 | "config": {
58 | "allow-plugins": {
59 | "dealerdirect/phpcodesniffer-composer-installer": true
60 | }
61 | },
62 | "scripts": {
63 | "cs-check": "phpcs --extensions=php",
64 | "cs-fix": "phpcbf --extensions=php",
65 | "lowest": " validate-prefer-lowest",
66 | "lowest-setup": "composer update --prefer-lowest --prefer-stable --prefer-dist --no-interaction && cp composer.json composer.backup && composer require --dev dereuromark/composer-prefer-lowest && mv composer.backup composer.json",
67 | "stan": "phpstan analyse",
68 | "stan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan:^2.0.0 && mv composer.backup composer.json",
69 | "test": "phpunit",
70 | "test-coverage": "phpunit --log-junit tmp/coverage/unitreport.xml --coverage-html tmp/coverage --coverage-clover tmp/coverage/coverage.xml"
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/config/app.default.php:
--------------------------------------------------------------------------------
1 | [
5 | 'blacklistedTables' => [],
6 | 'defaultTable' => null,
7 | ],
8 | ];
9 |
--------------------------------------------------------------------------------
/config/bootstrap.php:
--------------------------------------------------------------------------------
1 | ';
69 | var_dump($var);
70 | echo '';
71 |
72 | $backtrace = debug_backtrace(false, 1);
73 | pr('vd-location: ' . $backtrace[0]['file'] . ':' . $backtrace[0]['line']);
74 | }
75 | }
76 |
77 | if (!function_exists('vdd')) {
78 | /**
79 | * @param mixed $var
80 | * @return void
81 | */
82 | function vdd($var) {
83 | if (!Configure::read('debug')) {
84 | return;
85 | }
86 |
87 | echo '
';
88 | var_dump($var);
89 | echo '
';
90 |
91 | $backtrace = debug_backtrace(false, 1);
92 | pr('vdd-location: ' . $backtrace[0]['file'] . ':' . $backtrace[0]['line']);
93 | exit();
94 | }
95 | }
96 |
97 | if (PHP_SAPI === 'cli') {
98 | EventManager::instance()->on('Bake.initialize', function (EventInterface $event): void {
99 | $event->getSubject()->loadHelper('Setup.SetupBake');
100 | });
101 | }
102 |
--------------------------------------------------------------------------------
/docs/Component/Setup.md:
--------------------------------------------------------------------------------
1 | # Setup Component
2 |
3 | Attach this to your AppController to power up debugging:
4 | - Quick-Switch: layout, maintenance, debug, clearcache (password protected in production mode)
5 | - Notify Admin via Email about self-inflicted 404s or loops (configurable)
6 |
7 | TODO:
8 | - Catch redirect loops with meaningful exception (will also be logged then)
9 | - Automatically create stats about memory, exec time, queries and alike
10 |
11 | Note that debug, clearcache, maintenance etc for productive mode, since they require a password,
12 | are emergency commands only (in case you cannot power up ssh shell access that quickly).
13 | Change your password immediately afterwards for security reasons as pwds should not be passed
14 | plain via url.
15 |
16 | Tip: Use the CLI and the Setup plugin shells for normal execution.
17 |
18 | ## How to setup
19 | ```php
20 | // In your (App) controller
21 | public function initialize() {
22 | $this->loadComponent('Setup.Setup');
23 | }
24 | ```
25 |
--------------------------------------------------------------------------------
/docs/Console/Bake.md:
--------------------------------------------------------------------------------
1 | # Setup plugin Bake templates
2 |
3 | You can use these templates by using the `--theme`/`-t` option:
4 | ```
5 | bin/cake bake -t Setup ...
6 | ```
7 |
8 | See [CakePHP docs](https://book.cakephp.org/bake/2/en/development.html) on how to further customize.
9 |
10 | ## Model level
11 | By default uses Tools plugin Entity and Table classes in use statements.
12 | You can easily avoid this by having an `App\Model\Entity\Entity` and/or `App\Model\Table\Table` base class in your application.
13 | It will then detect those and use them instead. In those files you can then extend the Tools, Shim or CakePHP core classes.
14 |
15 | E.g. for your `App\Model\Entity\Entity`:
16 | ```php
17 | namespace App\Model\Entity;
18 |
19 | use Cake\ORM\Entity as CakeEntity;
20 |
21 | class Entity extends CakeEntity {
22 | }
23 | ```
24 |
25 | ## Controller level
26 | By default, it uses compact() to pass down variables. Makes debugging them easier in the controller scope.
27 |
28 | ## View level
29 | A few additional Table class configs can be added to customize field output.
30 |
31 | By default, the index pages do not print out the primary key(s). This is usually irrelevant info and can also be retrieved from the action links if necessary.
32 | Saves some column space.
33 |
34 | ### Skipping fields
35 | Use `public $skipFields = [...]` in your Table class to skip certain fields from being outputted.
36 | By default, the following fields are being excluded:
37 | ```php
38 | ['password', 'slug', 'lft', 'rght', 'created_by', 'modified_by', 'approved_by', 'deleted_by']
39 | ```
40 |
41 | Use one of the following to skip for a specific action type:
42 | - `$scaffoldSkipFieldsIndex`
43 | - `$scaffoldSkipFieldsView`
44 | - `$scaffoldSkipFieldsForm`
45 |
46 | ### Pagination order defaults
47 | `$paginationOrderReversedFields` and `$paginationOrderReversedFieldTypes` help to set the defaults for column sorting in paginations.
48 | The defaults currently are:
49 | ```php
50 | $paginationOrderReversedFields = ['published', 'rating', 'priority'];
51 | $paginationOrderReversedFieldTypes = ['datetime', 'date', 'time', 'bool'];
52 | ```
53 |
54 | ### Better auto-display of fields
55 | Text fields do not show up on index to avoid overcrowding.
56 |
57 | The following fields are auto-detected on top of the existing ones and formatted accordingly:
58 |
59 | - tinyint(2) of type enum as per Tools plugin enum solution (plural static method of the field): $entity::$method()
60 | - ['date', 'time', 'datetime', 'timestamp']: $this->Time->nice()
61 | - ['boolean']: $this->Format->yesNo() using Tools.Format helper
62 |
63 | ## TODO
64 | - The grouping of all fields has been removed, there is only text (paragraphs at the end) and default (all other field types) now.
65 | - Custom sorting of fields, if the DB cannot provide a good default order, auto-prio displayField as 1st, created/modified as last.
66 | - port $schema[$field]['type'] === 'decimal' || $schema[$field]['type'] === 'float' && strpos($schema[$field]['length'], ',2') as money formatting
67 |
--------------------------------------------------------------------------------
/docs/Controller/Setup.md:
--------------------------------------------------------------------------------
1 | # Setup Web Backend
2 |
3 | Useful tooling via `/admin/setup` route.
4 |
5 | **Important**: Make sure this is properly ACL protected (e.g. [TinyAuth](https://github.com/dereuromark/cakephp-tinyauth) and 3 lines of config) if you enable the routing here.
6 | Those actions should never be exposed to the public or non-admins.
7 |
8 | ## Setup
9 | For this you need to make sure the plugin is loaded with `['routes' => true]` or you provide them manually.
10 | Also make sure that the optional Tools dependency is in this case available.
11 |
12 | ## Useful tools
13 |
14 | ### Configuration Dashboard
15 | With useful info about server and application.
16 |
17 | See `/admin/setup/configuration`
18 |
19 | ### PHPInfo
20 | Lists the phpinfo() page as you know it.
21 |
22 | See `/admin/setup/backend/phpinfo`.
23 |
24 | ### Cache
25 | Details about the current cache config.
26 |
27 | See `/admin/setup/backend/cache`.
28 |
29 | ### Session
30 | Details about the current session config.
31 |
32 | See `/admin/setup/backend/session`.
33 |
34 | ### Cookies
35 | Details about the current cookies.
36 |
37 | See `/admin/setup/backend/cookies`.
38 |
39 | ### Database
40 | Overview about the current DB tables and size.
41 |
42 | See `/admin/setup/backend/database`.
43 |
44 | ### Foreign Keys
45 |
46 | Many applications for sure forgot to add proper constraints/handling around foreign keys:
47 |
48 | - NOT NULL foreign keys: Parent removes child if deleted
49 | - NULL: FK (parent_table_id) is set to NULL if parent gets deleted
50 |
51 | The first can be done both on DB level as constraint or Cake app level (using `depending true` config).
52 | The backend here now focuses on the 2nd part.
53 |
54 | #### FK nullable
55 | See `/admin/setup/database/foreign-keys`
56 | for an overview of all possible foreign keys and get a list of possible issues and checks to run.
57 |
58 | Make sure you apply the foreign key "NULL"able part to all existing rows in your DB.
59 | The script contains a check to make sure your DB is clean here before you apply those.
60 | The migration will otherwise fail to apply them.
61 |
62 | This is especially important when you want to find all childs that do not have some belongsTo relation (anymore) using
63 | `fk IS NULL`. You can only trust the results here if you have the sanity constraints here for cleanup of those fields on delete.
64 |
65 | A migration that could be proposed to you could look like this:
66 | ```php
67 | $this->table('repositories')
68 | ->addForeignKey('module_id', 'modules', ['id'], ['delete' => 'SET_NULL'])
69 | ->update();
70 | ```
71 | The `module_id` is `DEFAULT NULL` and as such, deleting now the module will auto-set this to false rather than keeping the old id (that cannot be joined in anymore).
72 |
73 | You can test the SQL issue on nullable and deleting live [here](http://sqlfiddle.com/#!9/816f16c/1).
74 |
--------------------------------------------------------------------------------
/docs/Healthcheck/Healthcheck.md:
--------------------------------------------------------------------------------
1 | # Healthcheck Documentation
2 | This plugin provides a healthcheck stack that can be used to check the status
3 | of your application and its services.
4 |
5 | ## Config/Setup
6 | The simplest way is to use Configure `Setup.Healthcheck.checks`:
7 | ```php
8 | 'Setup' => [
9 | 'Healthcheck' => [
10 | 'checks' => [
11 | \Setup\Healthcheck\Check\Environment\PhpVersionCheck::class,
12 | \Setup\Healthcheck\Check\Core\CakeVersionCheck::class,
13 | // ...
14 | ],
15 | ],
16 | ],
17 | ```
18 | Once defined, this will replace the defaults.
19 | You can also use the `Setup.Healthcheck.checks` config to add your own checks.
20 |
21 | To only replace the ones you need, you can also merge with the defaults:
22 | ```php
23 | 'Setup' => [
24 | 'Healthcheck' => [
25 | 'checks' => [
26 | \Setup\Healthcheck\Check\Environment\PhpUploadLimitCheck::class => [
27 | 'min' => 64,
28 | ],
29 | // ...
30 | ] + \Setup\Healthcheck\HealthcheckCollector::defaultChecks(),
31 | ],
32 | ],
33 | ```
34 |
35 |
36 |
37 | If you need to pass configs to the checks, you can do so by using the `Setup.Healthcheck.checksConfig` config:
38 | ```php
39 | 'Setup' => [
40 | 'Healthcheck' => [
41 | 'checksConfig' => [
42 | \Setup\Healthcheck\Check\Core\CakeVersionCheck::class => [
43 | 'overrideComparisonChar' => '^',
44 | ],
45 | // ...
46 | ],
47 | ],
48 | ],
49 | ```
50 | You can also pass the instance of the check class instead of the class name as a string if needed.
51 |
52 |
53 | ## Usage
54 |
55 | You can use the healthcheck stack by accessing the `/setup/healthcheck` endpoint in your application.
56 | In debug mode you can see the issues in detail. In production mode, only the status is shown.
57 |
58 | For CLI you can run the command:
59 | ```bash
60 | bin/cake healthcheck
61 | ```
62 |
63 | You can also write a queue task to run the healthcheck periodically and log the results or
64 | on errors directly alert the admin(s).
65 | Using [QueueScheduler plugin](https://github.com/dereuromark/cakephp-queue-scheduler) you can directly
66 | add a scheduled task for it in the backend, e.g. every hour.
67 |
68 |
--------------------------------------------------------------------------------
/docs/Install.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | ## How to include
4 | Installing the Plugin is pretty much as with every other CakePHP Plugin.
5 |
6 | Require the plugin using Packagist/Composer by running this in your application's folder:
7 |
8 | composer require dereuromark/cakephp-setup
9 |
10 | Note that you can also use `require-dev` if you don't need it for production environments and only use the dev tools.
11 |
12 | If you want, however, to use certain shells like "User" in the productive environment, as well, please
13 | use `require` then.
14 | Maintenance Mode and additional SetupComponent functionality would also not be available, otherwise.
15 |
16 | Details @ https://packagist.org/packages/dereuromark/cakephp-setup
17 |
18 | This will load the plugin (within your boostrap file):
19 | ```php
20 | Plugin::load('Setup');
21 | ```
22 | or
23 | ```php
24 | Plugin::loadAll();
25 | ```
26 |
27 | The recommendation is to also include the bootstrap file to leverage the debug output functions:
28 | ```php
29 | Plugin::load('Setup', ['bootstrap' => true]);
30 | ```
31 |
--------------------------------------------------------------------------------
/docs/Maintenance/Uptime.md:
--------------------------------------------------------------------------------
1 | # Uptime route
2 |
3 | By default
4 | ```
5 | /admin/setup/uptime
6 | ```
7 |
8 | You can customize this on project level.
9 |
--------------------------------------------------------------------------------
/docs/Panel/L10nPanel.md:
--------------------------------------------------------------------------------
1 | ## DebugKit L10n Panel
2 | The Setup plugin ships with a useful DebugKit panel to show quickly the current localization status
3 | - Datetime
4 | - Date
5 | - Time
6 |
7 | ### Enable the panel
8 | Activate the panel in your config:
9 |
10 | ```php
11 | 'DebugKit' => [
12 | 'panels' => [
13 | ...
14 | 'Setup.L10n' => true,
15 | ],
16 | ],
17 | ```
18 |
19 | Now it should be visible in your DebugKit panel list.
20 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # CakePHP Setup Plugin Documentation
2 |
3 | ## Version notice
4 |
5 | This branch only works for **CakePHP 4.x**
6 |
7 | ## Installation
8 | * [Installation](Install.md)
9 |
10 | ## Detailed Documentation - Quicklinks
11 | * [Maintenance Mode](Maintenance/Maintenance.md)
12 | * [Setup Component](Component/Setup.md)
13 | * [Setup Web Backend](Controller/Setup.md)
14 | * [Useful Setup Commands](Console/Commands.md)
15 | * [Healthcheck](Healthcheck/Healthcheck.md)
16 | * [Uptime](Maintenance/Uptime.md)
17 | *
18 | ## Bake Templates Deluxe
19 | The highly advanced bake templates have been further enhanced and are part of this plugin.
20 | The defaults go well together with the Tools plugin, Bootstrap3+ and some other useful defaults.
21 | You can also just steal ideas, of course ;)
22 | * [Setup plugin Bake templates](Console/Bake.md)
23 |
24 | ## Useful debugging help
25 | The following are convenience wrappers to debug safely. They will only show output with debug true.
26 |
27 | * dd($data) = debug() + die() // This is now also in CakePHP 3.3+ directly :-)
28 | * prd($data) = pr() + die()
29 | * vd() = var_dump()
30 | * vdd($data) = var_dump() + die()
31 |
32 | They are available when you include the plugin's bootstrap at Plugin::load().
33 |
34 | ## Testing
35 | You can test using a local installation of phpunit or the phar version of it:
36 |
37 | cd .../cakephp-setup
38 | composer install // or: php composer.phar install
39 | composer test-setup
40 | composer test
41 |
42 | To test a specific file:
43 |
44 | php phpunit.phar /path/to/MyClass.php
45 |
46 | To test MySQL specific tests, run this before (you might have to adjust your connection details):
47 | ```
48 | export DB_URL="mysql://root:secret@127.0.0.1/cake_test"
49 | ```
50 | By default the tests use an SQLite DB.
51 |
52 | ## Tips
53 |
54 | Import Huge SQL file:
55 |
56 | ...\bin\mysql -u root dbname < dumpfilename.sql
57 |
58 | Same other direction:
59 |
60 | ...\bin\mysqldump -h host -u root -p dbname > dumpfilename.sql
61 |
--------------------------------------------------------------------------------
/phpcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | config/
7 | src/
8 | tests/
9 | /tests/test_files/
10 |
11 |
12 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | level: 8
3 | paths:
4 | - src/
5 |
6 | bootstrapFiles:
7 | - %rootDir%/../../../tests/bootstrap.php
8 | reportUnmatchedIgnoredErrors: false
9 | earlyTerminatingMethodCalls:
10 | Cake\Console\BaseCommand:
11 | - abort
12 | dynamicConstantNames:
13 | - WINDOWS
14 |
15 | ignoreErrors:
16 | - identifier: missingType.iterableValue
17 | - identifier: missingType.generics
18 | - identifier: include.fileNotFound
19 | - identifier: trait.unused
20 | - '#Parameter \#1 \$items of class .+Collection constructor expects iterable, .+StatementInterface given.#'
21 | - '#Call to function method\_exists\(\) with .+Entity.+ and .+statuses.+ will always evaluate to true.#'
22 | - '#Call to function property\_exists\(\) with .+Table and .+ will always evaluate to false.#'
23 | -
24 | message: '#If condition is always false.#'
25 | path: '%currentWorkingDirectory%/src/Shell/IndentShell.php'
26 |
--------------------------------------------------------------------------------
/src/Auth/AbstractPasswordHasher.php:
--------------------------------------------------------------------------------
1 |
20 | */
21 | protected array $_defaultConfig = [];
22 |
23 | /**
24 | * Constructor
25 | *
26 | * @param array $config Array of config.
27 | */
28 | public function __construct(array $config = []) {
29 | $this->setConfig($config);
30 | }
31 |
32 | /**
33 | * Generates password hash.
34 | *
35 | * @param string $password Plain text password to hash.
36 | * @return string The password hash
37 | */
38 | abstract public function hash(string $password): string;
39 |
40 | /**
41 | * Check hash. Generate hash from user provided password string or data array
42 | * and check against existing hash.
43 | *
44 | * @param string $password Plain text password to hash.
45 | * @param string $hashedPassword Existing hashed password.
46 | * @return bool True if hashes match else false.
47 | */
48 | abstract public function check(string $password, string $hashedPassword): bool;
49 |
50 | /**
51 | * Returns true if the password need to be rehashed, due to the password being
52 | * created with anything else than the passwords generated by this class.
53 | *
54 | * Returns true by default since the only implementation users should rely
55 | * on is the one provided by default in php 5.5+ or any compatible library
56 | *
57 | * @param string $password The password to verify
58 | * @return bool
59 | */
60 | public function needsRehash(string $password): bool {
61 | return password_needs_rehash($password, PASSWORD_DEFAULT);
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/Auth/DefaultPasswordHasher.php:
--------------------------------------------------------------------------------
1 |
22 | */
23 | protected array $_defaultConfig = [
24 | 'hashType' => PASSWORD_DEFAULT,
25 | 'hashOptions' => [],
26 | ];
27 |
28 | /**
29 | * Generates password hash.
30 | *
31 | * @psalm-suppress InvalidNullableReturnType
32 | * @link https://book.cakephp.org/4/en/controllers/components/authentication.html#hashing-passwords
33 | * @param string $password Plain text password to hash.
34 | * @return string Password hash or false on failure
35 | */
36 | public function hash(string $password): string {
37 | return password_hash(
38 | $password,
39 | $this->_config['hashType'],
40 | $this->_config['hashOptions'],
41 | );
42 | }
43 |
44 | /**
45 | * Check hash. Generate hash for user provided password and check against existing hash.
46 | *
47 | * @param string $password Plain text password to hash.
48 | * @param string $hashedPassword Existing hashed password.
49 | * @return bool True if hashes match else false.
50 | */
51 | public function check(string $password, string $hashedPassword): bool {
52 | return password_verify($password, $hashedPassword);
53 | }
54 |
55 | /**
56 | * Returns true if the password need to be rehashed, due to the password being
57 | * created with anything else than the passwords generated by this class.
58 | *
59 | * @param string $password The password to verify
60 | * @return bool
61 | */
62 | public function needsRehash(string $password): bool {
63 | return password_needs_rehash($password, $this->_config['hashType'], $this->_config['hashOptions']);
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/Auth/PasswordHasherFactory.php:
--------------------------------------------------------------------------------
1 | |string $passwordHasher Name of the password hasher or an array with
17 | * at least the key `className` set to the name of the class to use
18 | * @throws \RuntimeException If password hasher class not found or
19 | * it does not extend {@link \Setup\Auth\AbstractPasswordHasher}
20 | * @return \Setup\Auth\AbstractPasswordHasher Password hasher instance
21 | */
22 | public static function build($passwordHasher): AbstractPasswordHasher {
23 | $config = [];
24 | if (is_string($passwordHasher)) {
25 | $class = $passwordHasher;
26 | } else {
27 | $class = $passwordHasher['className'];
28 | $config = $passwordHasher;
29 | unset($config['className']);
30 | }
31 |
32 | $className = App::className('Setup.' . $class, 'Auth', 'PasswordHasher');
33 | if ($className === null) {
34 | throw new RuntimeException(sprintf('Password hasher class "%s" was not found.', $class));
35 | }
36 |
37 | $hasher = new $className($config);
38 | if (!($hasher instanceof AbstractPasswordHasher)) {
39 | throw new RuntimeException('Password hasher must extend AbstractPasswordHasher class.');
40 | }
41 |
42 | return $hasher;
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/Command/CliTestCommand.php:
--------------------------------------------------------------------------------
1 | out('Router::url(\'/\'): ' . PHP_EOL . "\t" . $url);
33 |
34 | $arrayUrl = ['controller' => 'Test'];
35 | if ($args->getOption('prefix')) {
36 | /** @var non-falsy-string $prefix */
37 | $prefix = $args->getOption('prefix');
38 | $arrayUrl['prefix'] = $prefix;
39 | }
40 | if ($args->getOption('plugin')) {
41 | /** @var non-falsy-string $plugin */
42 | $plugin = $args->getOption('plugin');
43 | $arrayUrl['plugin'] = $plugin;
44 | }
45 |
46 | $url = Router::url($arrayUrl);
47 | $text = $this->_urlToText($arrayUrl);
48 | $io->out('Router::url([' . $text . ']): ' . PHP_EOL . "\t" . $url);
49 |
50 | $io->out($io->nl());
51 | $io->out('Full base URLs:');
52 |
53 | $url = Router::url('/', true);
54 | $io->out('Router::url(\'/\', true): ' . PHP_EOL . "\t" . $url);
55 |
56 | $url = Router::url($arrayUrl, true);
57 | $io->out('Router::url([' . $text . '], true): ' . PHP_EOL . "\t" . $url);
58 | }
59 |
60 | /**
61 | * Hook action for defining this command's option parser.
62 | *
63 | * @param \Cake\Console\ConsoleOptionParser $parser The parser to be defined
64 | *
65 | * @return \Cake\Console\ConsoleOptionParser The built parser.
66 | */
67 | protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser {
68 | $parser = parent::buildOptionParser($parser);
69 | $parser->setDescription(static::getDescription());
70 |
71 | $parser->addOption('prefix', [
72 | 'short' => 'p',
73 | 'help' => 'Prefix.',
74 | ]);
75 | $parser->addOption('plugin', [
76 | 'short' => 'x',
77 | 'help' => 'Plugin.',
78 | ]);
79 |
80 | return $parser;
81 | }
82 |
83 | /**
84 | * @param array $arrayUrl
85 | * @return string
86 | */
87 | protected function _urlToText(array $arrayUrl): string {
88 | $url = [];
89 | foreach ($arrayUrl as $k => $v) {
90 | $url[] = "'$k' => '$v'";
91 | }
92 |
93 | return implode(', ', $url);
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/src/Command/CurrentConfigConfigureCommand.php:
--------------------------------------------------------------------------------
1 | addArgument('key');
38 | }
39 |
40 | /**
41 | * @param \Cake\Console\Arguments $args The command arguments.
42 | * @param \Cake\Console\ConsoleIo $io The console io
43 | * @return int|null|void The exit code or null for success
44 | */
45 | public function execute(Arguments $args, ConsoleIo $io) {
46 | $key = $args->getArgument('key');
47 | $config = Configure::read($key);
48 | if (is_array($config)) {
49 | ksort($config);
50 | }
51 | $type = Debugger::getType($config);
52 | if (is_array($config)) {
53 | $type .= ' and size of ' . count($config);
54 | }
55 | $io->out(print_r($config, true));
56 | $io->info('of type ' . $type);
57 |
58 | return CommandInterface::CODE_SUCCESS;
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/src/Command/CurrentConfigDisplayCommand.php:
--------------------------------------------------------------------------------
1 | info('Security Salt: ' . Security::getSalt());
40 | $io->info('Full Base URL: ' . Configure::read('App.fullBaseUrl'));
41 |
42 | $io->out();
43 |
44 | $time = new DateTime();
45 | $timestamp = $time->getTimestamp();
46 | $offset = (int)($time->getOffset() / HOUR);
47 | $io->info('Datetime: ' . $time->format(FORMAT_DB_DATETIME) . ' (' . date_default_timezone_get() . ') [GMT' . ($offset > 0 ? '+' . $offset : '-' . abs($offset)) . ']');
48 | $io->info('Timestamp: ' . $timestamp . ' => ' . (new DateTime(date(FORMAT_DB_DATETIME, $timestamp)))->format(FORMAT_DB_DATETIME));
49 |
50 | $io->out();
51 |
52 | $io->info('Email Config:');
53 | $config = (array)Mailer::getConfig('default');
54 | foreach ($config as $key => $value) {
55 | $io->out(' - ' . $key . ': ' . $value);
56 | }
57 |
58 | $io->out();
59 |
60 | $io->info('ENV:');
61 | foreach ($_ENV as $key => $value) {
62 | $io->out(' - ' . $key . ': ' . $value);
63 | }
64 |
65 | return CommandInterface::CODE_SUCCESS;
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/Command/CurrentConfigPhpinfoCommand.php:
--------------------------------------------------------------------------------
1 | out($phpinfo);
44 |
45 | return CommandInterface::CODE_SUCCESS;
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/Command/CurrentConfigValidateCommand.php:
--------------------------------------------------------------------------------
1 | out('### DB default ###');
39 | try {
40 | $db = ConnectionManager::get('default');
41 | $io->out(print_r($db->config(), true));
42 | } catch (Exception $e) {
43 | $io->err($e->getMessage());
44 | }
45 |
46 | $io->out();
47 | $io->out('### DB test ###');
48 | try {
49 | $db = ConnectionManager::get('test');
50 | $io->out(print_r($db->config(), true));
51 | } catch (Exception $e) {
52 | $io->err($e->getMessage());
53 | }
54 |
55 | $io->out();
56 | $io->out('### Cache ###');
57 |
58 | $configured = Cache::configured();
59 | foreach ($configured as $key) {
60 | $io->out($key . ':');
61 | $io->out((string)json_encode(Cache::getConfig($key), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
62 | }
63 |
64 | return CommandInterface::CODE_SUCCESS;
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/Command/DbInitCommand.php:
--------------------------------------------------------------------------------
1 | args = $args;
47 | $this->io = $io;
48 |
49 | $connection = $this->_getConnection((string)$args->getOption('connection'));
50 | $config = $connection->config();
51 | $name = substr($config['driver'], strrpos($config['driver'], '\\') + 1);
52 | $config['scheme'] = strtolower($name);
53 | if ($config['scheme'] === 'sqlite' && $config['database'] === ':memory:') {
54 | $this->io->warning('Using in-memory database, skipping.');
55 |
56 | return;
57 | }
58 |
59 | $dsn = Text::insert('{scheme}://{username}:{password}@{host}', $config, ['before' => '{', 'after' => '}']);
60 | ConnectionManager::setConfig('setup', ['url' => $dsn]);
61 |
62 | /** @var \Cake\Database\Connection $connection */
63 | $connection = ConnectionManager::get('setup');
64 | $connection->execute('CREATE DATABASE IF NOT EXISTS ' . $config['database'] . ' ' .
65 | 'DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;');
66 |
67 | $this->io->success('Done: ' . $config['database']);
68 | }
69 |
70 | /**
71 | * @return \Cake\Console\ConsoleOptionParser
72 | */
73 | public function getOptionParser(): ConsoleOptionParser {
74 | $options = [
75 | 'connection' => [
76 | 'short' => 'c',
77 | 'help' => 'The datasource connection to use.',
78 | 'default' => 'default',
79 | ],
80 | ];
81 |
82 | return parent::getOptionParser()
83 | ->setDescription(static::getDescription())
84 | ->addOptions($options);
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/src/Command/DbResetCommand.php:
--------------------------------------------------------------------------------
1 | args = $args;
47 | $this->io = $io;
48 |
49 | $db = $this->_getConnection((string)$args->getOption('connection'));
50 |
51 | /** @var \Cake\Database\Schema\Collection $schemaCollection */
52 | $schemaCollection = $db->getSchemaCollection();
53 | $sources = $schemaCollection->listTables();
54 | foreach ($sources as $key => $source) {
55 | if ($source === 'phinxlog' || str_contains($source, '_phinxlog')) {
56 | unset($sources[$key]);
57 | }
58 | }
59 |
60 | $tableTruncates = 'TRUNCATE TABLE ' . implode(';' . PHP_EOL . 'TRUNCATE TABLE ', $sources) . ';';
61 |
62 | $sql = <<io->out('--------', 1, ConsoleIo::VERBOSE);
70 | $this->io->out($sql, 1, ConsoleIo::VERBOSE);
71 | $this->io->out('--------', 1, ConsoleIo::VERBOSE);
72 |
73 | $this->io->out('Truncating ' . count($sources) . ' tables');
74 | if (!$this->args->getOption('dry-run') && !$this->args->getOption('force')) {
75 | $looksGood = $this->io->askChoice('Sure?', ['y', 'n'], 'y');
76 | if ($looksGood !== 'y') {
77 | $this->io->abort('Aborted!');
78 | }
79 | }
80 |
81 | if (!$this->args->getOption('dry-run')) {
82 | $db->execute($sql);
83 | }
84 |
85 | $this->io->out('Done ' . ($this->args->getOption('dry-run') ? 'DRY-RUN' : '') . ' :)');
86 | }
87 |
88 | /**
89 | * @return \Cake\Console\ConsoleOptionParser
90 | */
91 | public function getOptionParser(): ConsoleOptionParser {
92 | $options = [
93 | 'dry-run' => [
94 | 'short' => 'd',
95 | 'help' => 'Dry run the reset, no data will be removed.',
96 | 'boolean' => true,
97 | ],
98 | 'force' => [
99 | 'short' => 'f',
100 | 'help' => 'Force the command, do not ask for confirmation.',
101 | 'boolean' => true,
102 | ],
103 | 'connection' => [
104 | 'short' => 'c',
105 | 'help' => 'The datasource connection to use.',
106 | 'default' => 'default',
107 | ],
108 | ];
109 |
110 | return parent::getOptionParser()
111 | ->setDescription(static::getDescription() . ' Note: It disables foreign key checks to do this.')
112 | ->addOptions($options);
113 | }
114 |
115 | }
116 |
--------------------------------------------------------------------------------
/src/Command/DbWipeCommand.php:
--------------------------------------------------------------------------------
1 | args = $args;
47 | $this->io = $io;
48 |
49 | $db = $this->_getConnection((string)$args->getOption('connection'));
50 |
51 | /** @var \Cake\Database\Schema\Collection $schemaCollection */
52 | $schemaCollection = $db->getSchemaCollection();
53 | $sources = $schemaCollection->listTables();
54 |
55 | $tableTruncates = 'DROP TABLE ' . implode(';' . PHP_EOL . 'DROP TABLE ', $sources) . ';';
56 |
57 | $sql = <<io->out('--------', 1, ConsoleIo::VERBOSE);
65 | $this->io->out($sql, 1, ConsoleIo::VERBOSE);
66 | $this->io->out('--------', 1, ConsoleIo::VERBOSE);
67 |
68 | $this->io->out('Dropping ' . count($sources) . ' tables');
69 | if (!$this->args->getOption('dry-run') && !$this->args->getOption('force')) {
70 | $looksGood = $this->io->askChoice('Sure?', ['y', 'n'], 'y');
71 | if ($looksGood !== 'y') {
72 | $this->io->abort('Aborted!');
73 | }
74 | }
75 |
76 | if (!$this->args->getOption('dry-run')) {
77 | $db->execute($sql);
78 | }
79 |
80 | $this->io->out('Done ' . ($this->args->getOption('dry-run') ? 'DRY-RUN' : '') . ' :)');
81 | }
82 |
83 | /**
84 | * @return \Cake\Console\ConsoleOptionParser
85 | */
86 | public function getOptionParser(): ConsoleOptionParser {
87 | $options = [
88 | 'dry-run' => [
89 | 'short' => 'd',
90 | 'help' => 'Dry run the reset, no tables will be removed.',
91 | 'boolean' => true,
92 | ],
93 | 'force' => [
94 | 'short' => 'f',
95 | 'help' => 'Force the command, do not ask for confirmation.',
96 | 'boolean' => true,
97 | ],
98 | 'connection' => [
99 | 'short' => 'c',
100 | 'help' => 'The datasource connection to use.',
101 | 'default' => 'default',
102 | ],
103 | ];
104 |
105 | return parent::getOptionParser()
106 | ->setDescription(static::getDescription() . ' Note: It disables foreign key checks to do this.')
107 | ->addOptions($options);
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/src/Command/MailCheckCommand.php:
--------------------------------------------------------------------------------
1 | warning('Configure::read(\'Config.live\') is not enabled. Normal emails wouldn\'t be sent. Overwriting it for this check only.');
26 | Configure::write('Config.live', true);
27 | }
28 |
29 | $io->verbose('From email as configured: ' . Configure::read('Config.systemEmail', Configure::read('Config.adminEmail')));
30 |
31 | $to = $io->ask('Email to send to', Configure::read('Config.adminEmail'));
32 | if (!$to) {
33 | return static::CODE_ERROR;
34 | }
35 |
36 | $email = new Mailer();
37 | $email->setTo($to);
38 | $email->setSubject('Test Mail from CLI');
39 |
40 | $url = Router::url('/', true);
41 | $message = <<deliver($message);
48 | $io->verbose('Result:');
49 | $io->verbose((string)json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
50 |
51 | return static::CODE_SUCCESS;
52 | }
53 |
54 | /**
55 | * Get the option parser.
56 | *
57 | * @return \Cake\Console\ConsoleOptionParser
58 | */
59 | public function getOptionParser(): ConsoleOptionParser {
60 | $parser = parent::getOptionParser();
61 |
62 | return $parser;
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/Command/MaintenanceModeActivateCommand.php:
--------------------------------------------------------------------------------
1 | Maintenance = new Maintenance();
36 | }
37 |
38 | /**
39 | * Implement this action with your command's logic.
40 | *
41 | * @param \Cake\Console\Arguments $args The command arguments.
42 | * @param \Cake\Console\ConsoleIo $io The console io
43 | * @return int|null|void The exit code or null for success
44 | */
45 | public function execute(Arguments $args, ConsoleIo $io) {
46 | $this->Maintenance->setMaintenanceMode(true);
47 |
48 | $io->out('Maintenance mode activated ...');
49 | }
50 |
51 | /**
52 | * Hook action for defining this command's option parser.
53 | *
54 | * @param \Cake\Console\ConsoleOptionParser $parser The parser to be defined
55 | * @return \Cake\Console\ConsoleOptionParser The built parser.
56 | */
57 | protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser {
58 | $parser = parent::buildOptionParser($parser);
59 |
60 | return $parser;
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/src/Command/MaintenanceModeDeactivateCommand.php:
--------------------------------------------------------------------------------
1 | Maintenance = new Maintenance();
33 | }
34 |
35 | /**
36 | * Implement this action with your command's logic.
37 | *
38 | * @param \Cake\Console\Arguments $args The command arguments.
39 | * @param \Cake\Console\ConsoleIo $io The console io
40 | * @return int|null|void The exit code or null for success
41 | */
42 | public function execute(Arguments $args, ConsoleIo $io) {
43 | $this->Maintenance->setMaintenanceMode(false);
44 | $io->out('Maintenance mode deactivated ...');
45 | }
46 |
47 | /**
48 | * Hook action for defining this command's option parser.
49 | *
50 | * @param \Cake\Console\ConsoleOptionParser $parser The parser to be defined
51 | * @return \Cake\Console\ConsoleOptionParser The built parser.
52 | */
53 | protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser {
54 | $parser = parent::buildOptionParser($parser);
55 |
56 | $parser->addOption('force', [
57 | 'short' => 'f',
58 | 'help' => 'Force (reset)',
59 | 'boolean' => true,
60 | ]);
61 |
62 | return $parser;
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/Command/MaintenanceModeStatusCommand.php:
--------------------------------------------------------------------------------
1 | Maintenance = new Maintenance();
33 | }
34 |
35 | /**
36 | * Implement this action with your command's logic.
37 | *
38 | * @param \Cake\Console\Arguments $args The command arguments.
39 | * @param \Cake\Console\ConsoleIo $io The console io
40 | * @return int|null|void The exit code or null for success
41 | */
42 | public function execute(Arguments $args, ConsoleIo $io) {
43 | $isMaintenanceMode = $this->Maintenance->isMaintenanceMode();
44 | if ($isMaintenanceMode) {
45 | $io->out('Maintenance mode active!');
46 | } else {
47 | $io->out('Maintenance mode not active');
48 | }
49 | }
50 |
51 | /**
52 | * Hook action for defining this command's option parser.
53 | *
54 | * @param \Cake\Console\ConsoleOptionParser $parser The parser to be defined
55 | * @return \Cake\Console\ConsoleOptionParser The built parser.
56 | */
57 | protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser {
58 | $parser = parent::buildOptionParser($parser);
59 |
60 | return $parser;
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/src/Command/MaintenanceModeWhitelistCommand.php:
--------------------------------------------------------------------------------
1 | Maintenance = new Maintenance();
34 | }
35 |
36 | /**
37 | * Implement this action with your command's logic.
38 | *
39 | * @param \Cake\Console\Arguments $args The command arguments.
40 | * @param \Cake\Console\ConsoleIo $io The console io
41 | * @return int|null|void The exit code or null for success
42 | */
43 | public function execute(Arguments $args, ConsoleIo $io) {
44 | $ip = $args->getArgument('ip');
45 | if ($ip) {
46 | if (!Validation::ipOrSubnet($ip)) {
47 | $io->abort($ip . ' is not a valid IP address or subnet.');
48 | }
49 | if ($args->getOption('remove')) {
50 | $this->Maintenance->clearWhitelist([$ip]);
51 | } else {
52 | $this->Maintenance->addToWhitelist([$ip]);
53 | }
54 | $io->out('Done!', 2);
55 | } else {
56 | if ($args->getOption('remove')) {
57 | $this->Maintenance->clearWhitelist();
58 | }
59 | }
60 |
61 | $io->out('Current whitelist:');
62 | /** @var array $ip */
63 | $ip = $this->Maintenance->whitelist();
64 | if (!$ip) {
65 | $io->out('n/a');
66 | } else {
67 | $io->out($ip);
68 | }
69 |
70 | return null;
71 | }
72 |
73 | /**
74 | * Hook action for defining this command's option parser.
75 | *
76 | * @param \Cake\Console\ConsoleOptionParser $parser The parser to be defined
77 | * @return \Cake\Console\ConsoleOptionParser The built parser.
78 | */
79 | protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser {
80 | $parser = parent::buildOptionParser($parser);
81 |
82 | $parser->addArgument('ip', [
83 | 'help' => 'IP address (or subnet) to specify.',
84 | ]);
85 |
86 | $parser->addOption('remove', [
87 | 'short' => 'r',
88 | 'help' => 'Remove either all or specific IPs.',
89 | 'boolean' => true,
90 | ]);
91 | $parser->addOption('debug', [
92 | 'short' => 'd',
93 | 'help' => 'Enable debug mode for whitelisted IPs.',
94 | 'boolean' => true,
95 | ]);
96 |
97 | return $parser;
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/src/Command/Traits/DbBackupTrait.php:
--------------------------------------------------------------------------------
1 | params['path'])) {
58 | $customPath = realpath($this->params['path']);
59 | if ($customPath) {
60 | $path = $customPath;
61 | }
62 | }
63 |
64 | return $path;
65 | }
66 |
67 | /**
68 | * Returns available files to restore
69 | * in reverse order (newest ones first!)
70 | *
71 | * @param string $path
72 | *
73 | * @return array Files
74 | */
75 | protected function _getFiles(string $path): array {
76 | $Directory = new RecursiveDirectoryIterator($path);
77 | $It = new RecursiveIteratorIterator($Directory);
78 | $Regex = new RegexIterator($It, '/\bbackup_.*?[\.sql|\.gz]$/', RecursiveRegexIterator::GET_MATCH);
79 | $files = [];
80 | foreach ($Regex as $v) {
81 | $files[] = $v[0];
82 | }
83 | $files = array_reverse($files);
84 |
85 | return $files;
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/src/Command/Traits/DbToolsTrait.php:
--------------------------------------------------------------------------------
1 | params['connection'])) {
24 | $name = $this->params['connection'];
25 | } elseif ($name === null) {
26 | $name = 'default';
27 | }
28 |
29 | /** @var \Cake\Database\Connection $connection */
30 | $connection = ConnectionManager::get($name);
31 |
32 | return $connection;
33 | }
34 |
35 | /**
36 | * @param string $prefix
37 | * @param string|null $connection
38 | *
39 | * @return array
40 | */
41 | protected function _getTables(string $prefix = '', ?string $connection = null): array {
42 | $db = $this->_getConnection($connection ?? 'default');
43 | $config = $db->config();
44 | $database = $config['database'];
45 |
46 | $script = "
47 | SELECT table_name
48 | FROM information_schema.tables AS tb
49 | WHERE table_schema = '$database'
50 | AND table_name LIKE '$prefix%' OR table_name LIKE '\_%';";
51 |
52 | $res = $db->execute($script)->fetchAll(Pdo::FETCH_ASSOC);
53 | if (!$res) {
54 | throw new RuntimeException('No tables found for DB `' . $database . '`...');
55 | }
56 |
57 | /** @var array $whitelist */
58 | $whitelist = []; //Text::tokenize((string)$this->args->getOption('table'));
59 |
60 | $tables = [];
61 | foreach ($res as $key => $table) {
62 | $tableName = $table['table_name'] ?? $table['TABLE_NAME'];
63 | if (str_starts_with($tableName, '_')) {
64 | continue;
65 | }
66 |
67 | if ($whitelist && !in_array($tableName, $whitelist)) {
68 | continue;
69 | }
70 |
71 | $tables[] = $tableName;
72 | }
73 |
74 | sort($tables);
75 |
76 | return $tables;
77 | }
78 |
79 | /**
80 | * @param string|null $model
81 | * @param string|null $plugin
82 | *
83 | * @throws \RuntimeException
84 | *
85 | * @return array<\Cake\ORM\Table>
86 | */
87 | protected function _getModels(?string $model, ?string $plugin): array {
88 | if ($model) {
89 | $className = App::className($plugin ? $plugin . '.' : $model, 'Model/Table', 'Table');
90 | if (!$className) {
91 | throw new RuntimeException('Model not found: ' . $model);
92 | }
93 |
94 | return [
95 | TableRegistry::getTableLocator()->get($plugin ? $plugin . '.' : $model),
96 | ];
97 | }
98 |
99 | $folders = App::classPath('Model/Table', $plugin);
100 |
101 | $models = [];
102 | foreach ($folders as $folder) {
103 | $folderContent = (new Folder($folder))->read(Folder::SORT_NAME, true);
104 |
105 | foreach ($folderContent[1] as $file) {
106 | $name = pathinfo($file, PATHINFO_FILENAME);
107 |
108 | preg_match('#^(.+)Table$#', $name, $matches);
109 | if (!$matches) {
110 | continue;
111 | }
112 |
113 | $model = $matches[1];
114 |
115 | $className = App::className($plugin ? $plugin . '.' . $model : $model, 'Model/Table', 'Table');
116 | if (!$className) {
117 | continue;
118 | }
119 |
120 | $models[] = TableRegistry::getTableLocator()->get($plugin ? $plugin . '.' . $model : $model);
121 | }
122 | }
123 |
124 | return $models;
125 | }
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/src/Command/UserUpdateCommand.php:
--------------------------------------------------------------------------------
1 | fetchModel(CLASS_USERS);
39 |
40 | $displayField = $Users->getDisplayField();
41 | if (!is_string($displayField)) {
42 | $io->abort('Only supported for single display fields');
43 | }
44 | $displayFieldName = Inflector::humanize($displayField);
45 |
46 | $displayFieldValue = $args->getArgument('login');
47 | while (!$displayFieldValue) {
48 | $displayFieldValue = $io->ask($displayFieldName);
49 | }
50 |
51 | /** @var \App\Model\Entity\User $user */
52 | $user = $Users->find()->where([$displayField => $displayFieldValue])->firstOrFail();
53 |
54 | $password = $args->getArgument('password');
55 | while (!$password) {
56 | $password = $io->ask('Password');
57 | }
58 |
59 | $Users->addBehavior('Tools.Passwordable', ['confirm' => false]);
60 | $Users->patchEntity($user, ['pwd' => $password]);
61 |
62 | if ($args->getOption('dry-run')) {
63 | $io->out('User dry-run inserted!');
64 | $io->out('Pwd Hash: ' . $user->password);
65 |
66 | return;
67 | }
68 |
69 | $Users->saveOrFail($user);
70 |
71 | $io->success('Password updated for user ' . $displayFieldValue);
72 | }
73 |
74 | /**
75 | * @return \Cake\Console\ConsoleOptionParser
76 | */
77 | public function getOptionParser(): ConsoleOptionParser {
78 | return parent::getOptionParser()
79 | ->setDescription('The User shell can create a user on the fly for local development.
80 | Note that you can define the constant CLASS_USERS in your bootstrap to point to another table class, if \'Users\' is not used.
81 | Make sure you configured the Passwordable behavior accordingly as per docs.')
82 | ->addArgument('login', [
83 | 'help' => 'Display field value',
84 | ])
85 | ->addArgument('password')
86 | ->addOption('dry-run', [
87 | 'short' => 'd',
88 | 'help' => 'Dry run the command, no data will actually be modified.',
89 | 'boolean' => true,
90 | ]);
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/src/Controller/Admin/ConfigurationController.php:
--------------------------------------------------------------------------------
1 | loadComponent('Flash');
27 | }
28 |
29 | /**
30 | * @return \Cake\Http\Response|null|void
31 | */
32 | public function index() {
33 | $Debug = new Debug();
34 | $uptime = $Debug->getUptime();
35 | $serverLoad = $Debug->serverLoad();
36 | $mem = $Debug->getRam();
37 | $memory = 'n/a';
38 | if ($mem) {
39 | $memory = '' . $mem['total'] . ' MB total; ' . $mem['free'] . ' MB free';
40 | }
41 | $this->set(compact('serverLoad', 'memory', 'uptime'));
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/Controller/Admin/DatabaseController.php:
--------------------------------------------------------------------------------
1 | getConnection();
25 |
26 | if (!$table) {
27 | $dbTables = $db->execute('SHOW TABLE STATUS')->fetchAll(PDO::FETCH_ASSOC);
28 | $dbTables = (new Collection($dbTables))->toArray();
29 | } else {
30 | $dbTables = [
31 | [
32 | 'Name' => $table,
33 | ],
34 | ];
35 | }
36 |
37 | $tables = [];
38 | foreach ($dbTables as $dbTable) {
39 | if (preg_match('/phinxlog$/', $dbTable['Name'])) {
40 | continue;
41 | }
42 | $blacklist = Configure::read('Setup.blacklistedTables');
43 | if ($blacklist && in_array($dbTable['Name'], $blacklist, true)) {
44 | continue;
45 | }
46 |
47 | $name = $dbTable['Name'];
48 | $Model = TableRegistry::getTableLocator()->get($name, ['allowFallbackClass' => true]);
49 |
50 | $schema = $Model->getSchema();
51 | $tables[$dbTable['Name']] = [
52 | 'schema' => $schema,
53 | ];
54 | }
55 |
56 | $this->set(compact('tables'));
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/src/Controller/Admin/SetupController.php:
--------------------------------------------------------------------------------
1 | request->is('post')) {
29 | $enable = (bool)$this->request->getQuery('maintenance');
30 |
31 | $maintenance->addToWhitelist([$ip]);
32 | $maintenance->setMaintenanceMode($enable);
33 |
34 | return $this->redirect([]);
35 | }
36 |
37 | $isMaintenanceModeEnabled = $maintenance->isMaintenanceMode();
38 | $whitelisted = $maintenance->whitelisted($ip);
39 | $whitelist = $maintenance->whitelist();
40 |
41 | $this->set(compact('ip', 'isMaintenanceModeEnabled', 'whitelisted', 'whitelist'));
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/Controller/Admin/UptimeController.php:
--------------------------------------------------------------------------------
1 | components()->has('Auth') && method_exists($this->components()->get('Auth'), 'allow')) {
23 | $this->components()->get('Auth')->allow();
24 | }
25 | }
26 |
27 | /**
28 | * @return \Cake\Http\Response|null
29 | */
30 | public function index() {
31 | $response = '';
32 | if (!isset($_REQUEST['key'])) {
33 | $response = 'OK';
34 | } else {
35 | if (preg_match('/^[a-f0-9]{32}$/', $_REQUEST['key'])) {
36 | $response = $_REQUEST['key'];
37 | }
38 | }
39 |
40 | return $this->response->withStringBody($response);
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/Controller/HealthcheckController.php:
--------------------------------------------------------------------------------
1 | components()->has('Auth') && method_exists($this->components()->get('Auth'), 'allow')) {
26 | $this->components()->get('Auth')->allow();
27 | } elseif ($this->components()->has('Authentication') && method_exists($this->components()->get('Authentication'), 'allowUnauthenticated')) {
28 | $this->components()->get('Authentication')->allowUnauthenticated(['index']);
29 | }
30 | if ($this->components()->has('Authorization') && method_exists($this->components()->get('Authorization'), 'skipAuthorization')) {
31 | $this->components()->get('Authorization')->skipAuthorization();
32 | }
33 | }
34 |
35 | /**
36 | * @return \Cake\Http\Response|null|void
37 | */
38 | public function index() {
39 | $healthcheck = new Healthcheck(new HealthcheckCollector());
40 | $passed = $healthcheck->run($this->request->getQuery('domain'));
41 |
42 | if ($this->request->is('json')) {
43 | $data = [
44 | 'passed' => $passed,
45 | ];
46 | if (Configure::read('debug')) {
47 | $data['result'] = $healthcheck->result();
48 | }
49 |
50 | return $this->response->withType('application/json')
51 | ->withStringBody(json_encode($data, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
52 | }
53 |
54 | if (!Configure::read('debug')) {
55 | return $this->response->withStringBody($passed ? 'OK' : 'FAIL')
56 | ->withStatus($passed ? 200 : 500);
57 | }
58 |
59 | $result = $healthcheck->result();
60 | $domains = $healthcheck->domains();
61 | $this->set(compact('passed', 'result', 'domains'));
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/Healthcheck/Check/CheckInterface.php:
--------------------------------------------------------------------------------
1 |
40 | */
41 | public function scope(): array;
42 |
43 | /**
44 | * Returns the message to display when passed.
45 | *
46 | * @return array
47 | */
48 | public function successMessage(): array;
49 |
50 | /**
51 | * Returns the message to display when warnings occur.
52 | *
53 | * @return array
54 | */
55 | public function warningMessage(): array;
56 |
57 | /**
58 | * Returns the message to display when failed.
59 | *
60 | * @return array
61 | */
62 | public function failureMessage(): array;
63 |
64 | /**
65 | * Returns the message to display additional info.
66 | *
67 | * @return array
68 | */
69 | public function infoMessage(): array;
70 |
71 | /**
72 | * Returns the name of this check.
73 | *
74 | * @return string
75 | */
76 | public function name(): string;
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/Healthcheck/Check/Core/CakeCacheCheck.php:
--------------------------------------------------------------------------------
1 | passed = $this->assertCache();
29 | }
30 |
31 | /**
32 | * @return string[]
33 | */
34 | public function failureMessage(): array {
35 | return [
36 | 'The following cache setups are missing: ' . implode(', ', $this->missing) . '.',
37 | ];
38 | }
39 |
40 | /**
41 | * @return bool
42 | */
43 | protected function assertCache(): bool {
44 | $cacheKeys = $this->defaultCacheKeys;
45 | $additional = (array)Configure::read('Healthcheck.checkCacheKeys');
46 | foreach ($additional as $key) {
47 | if (!in_array($key, $cacheKeys, true)) {
48 | $cacheKeys[] = $key;
49 | }
50 | }
51 |
52 | $issues = [];
53 | foreach ($cacheKeys as $cacheKey) {
54 | if (Cache::getConfig($cacheKey)) {
55 | continue;
56 | }
57 |
58 | $issues[] = $cacheKey;
59 | }
60 |
61 | $this->missing = $issues;
62 |
63 | return !$this->missing;
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/Healthcheck/Check/Core/CakeSaltCheck.php:
--------------------------------------------------------------------------------
1 | passed = $this->assertSalt();
20 | }
21 |
22 | /**
23 | * @return string[]
24 | */
25 | public function failureMessage(): array {
26 | return [
27 | 'Security.salt is not set up yet.',
28 | ];
29 | }
30 |
31 | /**
32 | * @return bool
33 | */
34 | protected function assertSalt(): bool {
35 | $salt = Configure::read('Security.salt');
36 |
37 | return $salt !== '__SALT__';
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/Healthcheck/Check/Core/CakeVersionCheck.php:
--------------------------------------------------------------------------------
1 | cakeVersion = $cakeVersion;
38 | $this->root = $root;
39 | }
40 |
41 | /**
42 | * @return void
43 | */
44 | public function check(): void {
45 | $this->passed = $this->assertVersion();
46 | }
47 |
48 | /**
49 | * @return bool
50 | */
51 | protected function assertVersion(): bool {
52 | $lockFile = $this->root . 'composer.lock';
53 | if (!is_file($lockFile)) {
54 | $this->warningMessage[] = 'You need to create the lock file first using composer install: ' . $lockFile;
55 |
56 | return false;
57 | }
58 |
59 | $content = json_decode((string)file_get_contents($lockFile), true);
60 | $packages = Hash::combine($content['packages'], '{n}.name', '{n}.version');
61 | $version = $packages['cakephp/cakephp'] ?? null;
62 | if (!$version) {
63 | $this->warningMessage[] = 'CakePHP does not seem installed as require dependency.';
64 |
65 | return false;
66 | }
67 |
68 | if (str_starts_with($version, 'dev-') || str_ends_with($version, '-dev')) {
69 | $this->infoMessage[] = 'CakePHP is installed as dev version `' . $version . '`, which is not recommended for production.';
70 |
71 | return true;
72 | }
73 |
74 | if (class_exists(Semver::class)) {
75 | if (!Semver::satisfies($this->cakeVersion, $version)) {
76 | $this->failureMessage[] = 'Installed CakePHP version `' . $this->cakeVersion . '` does not match lock file version `' . $version . '`';
77 |
78 | return false;
79 | }
80 |
81 | $this->infoMessage[] = $this->cakeVersion;
82 |
83 | return true;
84 | }
85 |
86 | $result = $this->cakeVersion === $version;
87 | if (!$result) {
88 | $this->failureMessage[] = 'PHP version `' . $this->cakeVersion . '` does not match lock file version `' . $version . '`';
89 |
90 | return false;
91 | }
92 |
93 | $this->infoMessage[] = $this->cakeVersion;
94 |
95 | return true;
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/src/Healthcheck/Check/Core/FullBaseUrlCheck.php:
--------------------------------------------------------------------------------
1 | checkFullBaseUrl();
15 | }
16 |
17 | /**
18 | * @return void
19 | */
20 | protected function checkFullBaseUrl(): void {
21 | $fullBaseUrl = Configure::read('App.fullBaseUrl');
22 | if (!$fullBaseUrl) {
23 | $this->failureMessage[] = 'App.fullBaseUrl is not set. Please configure it in your app.php or .env file.';
24 | $this->passed = false;
25 |
26 | return;
27 | }
28 |
29 | $this->infoMessage[] = $fullBaseUrl;
30 |
31 | $this->passed = true;
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/Healthcheck/Check/Database/ConnectCheck.php:
--------------------------------------------------------------------------------
1 | connection = $connection;
32 | }
33 |
34 | /**
35 | * @return void
36 | */
37 | public function check(): void {
38 | $this->assertConnection();
39 | }
40 |
41 | /**
42 | * @return void
43 | */
44 | protected function assertConnection(): void {
45 | try {
46 | /** @var \Cake\Database\Connection $connection */
47 | $connection = ConnectionManager::get($this->connection);
48 | $connection->getDriver()->connect();
49 | $this->passed = true;
50 | } catch (Exception $connectionError) {
51 | $this->passed = false;
52 | }
53 |
54 | if (!$this->passed) {
55 | $this->failureMessage[] = 'Cannot connect to database on connection `' . $this->connection . '`: ' . $connectionError->getMessage();
56 |
57 | return;
58 | }
59 |
60 | $this->infoMessage[] = 'The PHP upload limit is set to `' . ini_get('upload_max_filesize') . '` and the post limit is set to `' . ini_get('post_max_size') . '`.';
61 | }
62 |
63 | /**
64 | * @param string $val
65 | * @return int
66 | */
67 | protected function toBytes(string $val): int {
68 | $val = trim($val);
69 | $unit = strtolower($val[strlen($val) - 1]);
70 | $bytes = (int)$val;
71 |
72 | switch ($unit) {
73 | case 'g':
74 | $bytes *= 1024;
75 | // Continue
76 | case 'm':
77 | $bytes *= 1024;
78 | // Continue
79 | case 'k':
80 | $bytes *= 1024;
81 | // Continue
82 | }
83 |
84 | return $bytes;
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/src/Healthcheck/Check/Environment/PhpUploadLimitCheck.php:
--------------------------------------------------------------------------------
1 | min = $min;
30 | }
31 |
32 | /**
33 | * @return void
34 | */
35 | public function check(): void {
36 | $this->assertMinimum();
37 | }
38 |
39 | /**
40 | * @return void
41 | */
42 | protected function assertMinimum(): void {
43 | $uploadMax = $this->toBytes((string)ini_get('upload_max_filesize'));
44 | $postMax = $this->toBytes((string)ini_get('post_max_size'));
45 |
46 | $this->passed = true;
47 | $min = $this->min * 1024 * 1024; // Convert MB to bytes
48 | if ($min > $uploadMax) {
49 | $this->failureMessage[] = 'The PHP upload limit is too low. It is currently set to `' . ini_get('upload_max_filesize') . '`, but at least ' . $this->min . ' MB is required.';
50 |
51 | $this->passed = false;
52 | }
53 | if ($min > $postMax) {
54 | $this->failureMessage[] = 'The PHP post limit is too low. It is currently set to `' . ini_get('post_max_size') . '`, but at least ' . $this->min . ' MB is required.';
55 |
56 | $this->passed = false;
57 | }
58 |
59 | if (!$this->passed) {
60 | return;
61 | }
62 |
63 | $this->infoMessage[] = 'The PHP upload limit is set to `' . ini_get('upload_max_filesize') . '` and the post limit is set to `' . ini_get('post_max_size') . '`.';
64 | }
65 |
66 | /**
67 | * @param string $val
68 | * @return int
69 | */
70 | protected function toBytes(string $val): int {
71 | $val = trim($val);
72 | $unit = strtolower($val[strlen($val) - 1]);
73 | $bytes = (int)$val;
74 |
75 | switch ($unit) {
76 | case 'g':
77 | $bytes *= 1024;
78 | // Continue
79 | case 'm':
80 | $bytes *= 1024;
81 | // Continue
82 | case 'k':
83 | $bytes *= 1024;
84 | // Continue
85 | }
86 |
87 | return $bytes;
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/src/Healthcheck/Healthcheck.php:
--------------------------------------------------------------------------------
1 | collector = $collector;
25 | $this->result = new Collection([]);
26 | }
27 |
28 | /**
29 | * @param string|null $domain
30 | * @return bool
31 | */
32 | public function run(?string $domain = null): bool {
33 | $checks = $this->collector->getChecks();
34 | foreach ($checks as $check) {
35 | if ($domain && $check->domain() !== $domain) {
36 | continue;
37 | }
38 |
39 | $check->check();
40 | if (!$check->passed()) {
41 | $this->passed = false;
42 | }
43 |
44 | $this->result = $this->result->appendItem($check);
45 | }
46 |
47 | return $this->passed;
48 | }
49 |
50 | /**
51 | * @return \Cake\Collection\CollectionInterface
52 | */
53 | public function result(): CollectionInterface {
54 | return $this->result->groupBy(function (CheckInterface $result) {
55 | return $result->domain();
56 | });
57 | }
58 |
59 | /**
60 | * @return array
61 | */
62 | public function domains(): array {
63 | return $this->collector->getDomains();
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/Healthcheck/HealthcheckCollector.php:
--------------------------------------------------------------------------------
1 |
26 | */
27 | protected array $checks;
28 |
29 | /**
30 | * @return array, mixed>
31 | */
32 | public static function defaultChecks(): array {
33 | $checks = static::$defaultChecks;
34 |
35 | $result = [];
36 | foreach ($checks as $check) {
37 | $result[$check] = [];
38 | }
39 |
40 | return $result;
41 | }
42 |
43 | /**
44 | * @param array $checks
45 | */
46 | public function __construct(array $checks = []) {
47 | if (!$checks) {
48 | $checks = Configure::read('Setup.Healthcheck.checks', static::defaultChecks());
49 | }
50 |
51 | $this->checks = $this->buildChecks($checks);
52 | }
53 |
54 | /**
55 | * Returns the list of checks to be run.
56 | *
57 | * @return array<\Setup\Healthcheck\Check\CheckInterface>
58 | */
59 | public function getChecks(): array {
60 | return $this->checks;
61 | }
62 |
63 | /**
64 | * @return array
65 | */
66 | public function getDomains(): array {
67 | $domains = [];
68 | foreach ($this->checks as $check) {
69 | $domain = $check->domain();
70 | if (in_array($domain, $domains, true)) {
71 | continue;
72 | }
73 |
74 | $domains[] = $domain;
75 | }
76 |
77 | return $domains;
78 | }
79 |
80 | /**
81 | * @param array $checks
82 | * @return array<\Setup\Healthcheck\Check\CheckInterface>
83 | */
84 | protected function buildChecks(mixed $checks): array {
85 | $checkInstances = [];
86 | foreach ($checks as $class => $options) {
87 | if ($options === false) {
88 | continue;
89 | }
90 | if (is_object($options)) {
91 | /** @var \Setup\Healthcheck\Check\CheckInterface $options */
92 | $checkInstances[] = $options;
93 |
94 | continue;
95 | }
96 | if (is_numeric($class) && is_string($options)) {
97 | /** @var class-string<\Setup\Healthcheck\Check\CheckInterface> $options */
98 | $checkInstances[] = new $options();
99 |
100 | continue;
101 | }
102 |
103 | /** @var class-string<\Setup\Healthcheck\Check\CheckInterface> $class */
104 | $checkInstances[] = new $class(...$options);
105 | }
106 |
107 | return $checkInstances;
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/src/Middleware/MaintenanceMiddleware.php:
--------------------------------------------------------------------------------
1 | View::class,
26 | 'templatePath' => 'Error',
27 | 'statusCode' => 503,
28 | 'templateLayout' => false,
29 | 'templateFileName' => 'maintenance',
30 | 'templateExtension' => '.php',
31 | 'contentType' => 'text/html',
32 | ];
33 |
34 | /**
35 | * @param array $config
36 | */
37 | public function __construct(array $config = []) {
38 | $this->setConfig($config);
39 | }
40 |
41 | /**
42 | * @param \Cake\Http\ServerRequest $request
43 | * @param \Psr\Http\Server\RequestHandlerInterface $handler
44 | *
45 | * @return \Psr\Http\Message\ResponseInterface
46 | */
47 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
48 | $ip = $request->clientIp();
49 | $maintenance = new Maintenance();
50 |
51 | $response = $handler->handle($request);
52 | if (!$maintenance->isMaintenanceMode($ip)) {
53 | return $response;
54 | }
55 |
56 | $response = $this->build($response);
57 |
58 | return $response;
59 | }
60 |
61 | /**
62 | * @param \Psr\Http\Message\ResponseInterface $response
63 | * @return \Psr\Http\Message\ResponseInterface
64 | */
65 | protected function build(ResponseInterface $response) {
66 | $cakeRequest = ServerRequestFactory::fromGlobals();
67 | $builder = new ViewBuilder();
68 |
69 | $templateName = $this->getConfig('templateFileName');
70 | $templatePath = $this->getConfig('templatePath');
71 |
72 | $builder->setClassName($this->getConfig('className'))
73 | ->setTemplatePath(Inflector::camelize($templatePath));
74 | if (!$this->getConfig('templateLayout')) {
75 | $builder->disableAutoLayout();
76 | } else {
77 | $builder->setLayout($this->getConfig('templateLayout'));
78 | }
79 |
80 | $view = $builder
81 | ->build($cakeRequest)
82 | ->setConfig('_ext', $this->getConfig('templateExtension'));
83 |
84 | $bodyString = $view->render($templateName);
85 |
86 | $response = $response->withHeader('Retry-After', (string)HOUR)
87 | ->withHeader('Content-Type', $this->getConfig('contentType'))
88 | ->withStatus($this->getConfig('statusCode'));
89 |
90 | $body = new CallbackStream(function () use ($bodyString) {
91 | return $bodyString;
92 | });
93 |
94 | /** @var \Psr\Http\Message\ResponseInterface $maintenanceResponse */
95 | $maintenanceResponse = $response->withBody($body);
96 |
97 | return $maintenanceResponse;
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/src/Panel/L10nPanel.php:
--------------------------------------------------------------------------------
1 |
31 | */
32 | protected array $_defaultConfig = [
33 | ];
34 |
35 | /**
36 | * Data collection callback.
37 | *
38 | * @param \Cake\Event\EventInterface $event The shutdown event.
39 | *
40 | * @return void
41 | */
42 | public function shutdown(EventInterface $event): void {
43 | }
44 |
45 | /**
46 | * Get the data for this panel
47 | *
48 | * @return array
49 | */
50 | public function data(): array {
51 | $translator = I18n::getTranslator();
52 | $messages = $translator->getPackage()->getMessages();
53 |
54 | $data = [
55 | 'values' => [
56 | 'datetime' => new DateTime(),
57 | 'date' => new Date(),
58 | 'time' => new Time(),
59 | 'time-noon' => Time::noon(),
60 | 'time-midnight' => Time::midnight(),
61 | ],
62 | 'timezone' => [
63 | 'default' => Configure::read('App.defaultTimezone'),
64 | 'output' => Configure::read('App.defaultOutputTimezone'),
65 | 'current' => date_default_timezone_get(),
66 | ],
67 | 'currency' => [
68 | 'default currency' => Number::getDefaultCurrency(),
69 | 'formatted value' => Number::currency('12.34'),
70 | ],
71 | 'messages' => $messages,
72 | ];
73 |
74 | return $this->_data + $data;
75 | }
76 |
77 | /**
78 | * Get the summary data for a panel.
79 | *
80 | * This data is displayed in the toolbar even when the panel is collapsed.
81 | *
82 | * @return string
83 | */
84 | public function summary(): string {
85 | return '';
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/src/Queue/Task/HealthcheckTask.php:
--------------------------------------------------------------------------------
1 | $data The array passed to QueuedJobsTable::createJob()
16 | * @param int $jobId The id of the QueuedJob entity
17 | * @return void
18 | */
19 | public function run(array $data, int $jobId): void {
20 | $healthcheck = new Healthcheck(new HealthcheckCollector());
21 |
22 | $passed = $healthcheck->run($data['domain'] ?? null);
23 |
24 | $result = $healthcheck->result();
25 | $message = 'Healthcheck queue run ' . ($passed ? 'OK' : 'FAIL');
26 | $message .= PHP_EOL . PHP_EOL;
27 | /**
28 | * @var string $domain
29 | * @var \Setup\Healthcheck\Check\CheckInterface[] $checks
30 | */
31 | foreach ($result as $domain => $checks) {
32 | $message .= '### ' . $domain . PHP_EOL;
33 | foreach ($checks as $check) {
34 | $message .= ' - ' . $check->name() . ': ' . ($check->passed() ? 'OK' : 'FAIL') . PHP_EOL;
35 | if (!$check->passed()) {
36 | if ($check->failureMessage()) {
37 | $message .= ' Error: ' . implode(', ', $check->failureMessage()) . PHP_EOL;
38 | }
39 | if ($check->warningMessage()) {
40 | $message .= ' Warning: ' . implode(', ', $check->warningMessage()) . PHP_EOL;
41 | }
42 | } else {
43 | if ($check->successMessage()) {
44 | $message .= ' Passed: ' . implode(', ', $check->successMessage()) . PHP_EOL;
45 | }
46 | }
47 |
48 | if ($check->infoMessage()) {
49 | $message .= ' Info: ' . implode(', ', $check->infoMessage()) . PHP_EOL;
50 | }
51 | }
52 | }
53 |
54 | Log::write($passed ? 'info' : 'warning', $message);
55 | }
56 |
57 | /**
58 | * @param string|null $data
59 | * @return void
60 | */
61 | public function add(?string $data): void {
62 | $data = [
63 | 'domain' => $data,
64 | ];
65 | $this->QueuedJobs->createJob('Setup.Healthcheck', $data);
66 | $this->io->success('OK, job created, now run the worker');
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/TestSuite/DriverSkipTrait.php:
--------------------------------------------------------------------------------
1 | skipIf(strpos($config['driver'], $type) === false, $message);
20 | }
21 |
22 | /**
23 | * @param string $type
24 | * @param string $message
25 | * @return void
26 | */
27 | protected function skipIfDriver(string $type, string $message = '') {
28 | $config = ConnectionManager::getConfig('test');
29 | $this->skipIf(strpos($config['driver'], $type) !== false, $message);
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/Utility/ClassFinder.php:
--------------------------------------------------------------------------------
1 | |null $plugins
15 | *
16 | * @return array>
17 | */
18 | public static function get(string $string, ?array $plugins): array {
19 | $appPaths = App::classPath($string);
20 | $result = [];
21 | $classes = static::getClasses($appPaths);
22 | if ($classes) {
23 | $appNamespace = (string)Configure::readOrFail('App.namespace');
24 | $result[strtoupper($appNamespace)] = $classes;
25 | }
26 |
27 | if ($plugins === null) {
28 | $plugins = Plugin::loaded();
29 | }
30 | foreach ($plugins as $plugin) {
31 | $pluginPaths = App::classPath($string, $plugin);
32 | $classes = static::getClasses($pluginPaths);
33 | if ($classes) {
34 | $result[$plugin] = $classes;
35 | }
36 | }
37 |
38 | return $result;
39 | }
40 |
41 | /**
42 | * @param array $folders
43 | *
44 | * @return array
45 | */
46 | public static function getClasses(array $folders): array {
47 | $names = [];
48 | foreach ($folders as $folder) {
49 | $folderContent = (new Folder($folder))->read(Folder::SORT_NAME, true);
50 |
51 | foreach ($folderContent[1] as $file) {
52 | $name = pathinfo($file, PATHINFO_FILENAME);
53 | $names[] = $name;
54 | }
55 |
56 | foreach ($folderContent[0] as $subFolder) {
57 | $folderContent = (new Folder($folder . $subFolder))->read(Folder::SORT_NAME, true);
58 |
59 | foreach ($folderContent[1] as $file) {
60 | $name = pathinfo($file, PATHINFO_FILENAME);
61 | $names[] = $subFolder . '/' . $name;
62 | }
63 | }
64 | }
65 |
66 | return $names;
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/Utility/Config.php:
--------------------------------------------------------------------------------
1 | $v) {
71 | if (!is_array($v)) {
72 | $result[$k] = ['name' => $k, 'value' => $v, 'children' => []];
73 |
74 | continue;
75 | }
76 |
77 | $result[$k] = $v;
78 | $result[$k]['name'] = $k;
79 |
80 | if (is_string($k) && !empty($v)) {
81 | $result[$k]['children'] = static::configTree($v);
82 | $result[$k]['value'] = $v;
83 | } else {
84 | $result[$k]['children'] = [];
85 | $result[$k]['value'] = $v;
86 | }
87 | }
88 |
89 | return $result;
90 | }
91 |
92 | /**
93 | * Replaces sensitive strings with dummy text for security reasons.
94 | *
95 | * @param mixed $value
96 | *
97 | * @return mixed
98 | */
99 | protected static function value($value) {
100 | if (!is_string($value) || $value === '') {
101 | return $value;
102 | }
103 |
104 | if (in_array(strtoupper($value), ['0', '1', 'TRUE', 'FALSE'], true)) {
105 | return $value;
106 | }
107 |
108 | return $value;
109 | }
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/src/Utility/OrmTypes.php:
--------------------------------------------------------------------------------
1 | $classes) {
36 | $result = [];
37 | foreach ($classes as $class) {
38 | $namespacePrefix = str_replace('/', '\\', static::NAMESPACE);
39 | $fullClass = $namespacePrefix . '\\' . $class;
40 | if (strtoupper($namespace) !== strtoupper(Configure::readOrFail('App.namespace'))) {
41 | $fullClass = str_replace('/', '\\', $namespace) . '\\' . $fullClass;
42 | }
43 | $name = substr($class, 0, -strlen(static::SUFFIX));
44 |
45 | if ($exclude && in_array($fullClass, $exclude, true)) {
46 | continue;
47 | }
48 |
49 | $result[$name] = $fullClass;
50 | }
51 |
52 | $allClasses[$namespace] = $result;
53 | if (!$allClasses[$namespace]) {
54 | unset($allClasses[$namespace]);
55 | }
56 | }
57 |
58 | return $allClasses;
59 | }
60 |
61 | /**
62 | * @return array>
63 | */
64 | public static function getMap(): array {
65 | /** @var array $map */
66 | $map = TypeFactory::getMap();
67 |
68 | $result = [];
69 | foreach ($map as $type => $class) {
70 | $name = static::name($class);
71 |
72 | $result[$type] = [
73 | 'name' => $name,
74 | 'class' => $class,
75 | ];
76 | }
77 |
78 | return $result;
79 | }
80 |
81 | /**
82 | * @param string $class
83 | *
84 | * @return string
85 | */
86 | protected static function name(string $class): string {
87 | $namespace = str_replace('/', '\\', static::NAMESPACE);
88 | preg_match('#^(.+)\\\\' . preg_quote($namespace) . '\\\\(.+)' . preg_quote(static::SUFFIX) . '#', $class, $matches);
89 | if (!$matches || empty($matches[1]) || empty($matches[2])) {
90 | throw new RuntimeException('Invalid type class: ' . $class);
91 | }
92 |
93 | if ($matches[1] === 'Cake' || $matches[1] === Configure::readOrFail('App.namespace')) {
94 | return $matches[2];
95 | }
96 |
97 | return $matches[1] . '.' . $matches[2];
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/src/Utility/Setup.php:
--------------------------------------------------------------------------------
1 | = 0 && $mask <= 32;
30 | }
31 | if (in_array($type, ['both', 'ipv6', true]) && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
32 | return is_numeric($mask) && $mask >= 0 && $mask <= 128;
33 | }
34 |
35 | return false;
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/templates/Admin/Backend/cache.php:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
Cache Config
16 |
17 |
18 |
19 | $config) { ?>
20 |
24 | -
25 |
[]
26 |
27 |
28 | Data:
29 |
30 |
(Time->timeAgoInWords(new DateTime($data[$key])); ?>)
31 |
32 |
33 | Form->postLink('Store current time for testing', ['?' => ['key' => $key]], ['class' => 'button primary btn btn-primary']); ?>
34 |
35 |
36 |
37 |
38 | Details
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/templates/Admin/Backend/cookies.php:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
Cookies
13 |
14 | getIterator() as $cookie) {
17 | $expireDateTime = $cookie->getExpiry();
18 |
19 | echo '
' . $cookie->getName() . '
';
20 |
21 | echo '
';
22 | echo json_encode($cookie->toArray(), JSON_PRETTY_PRINT);
23 | echo '
';
24 |
25 | echo '
';
26 | echo 'Expires: ' . ($expireDateTime ? $this->Time->nice($expireDateTime) : 'n/a');
27 |
28 | echo ' ' . $this->Form->postLink('Delete', ['?' => ['cookie' => $cookie->getName()]], ['class' => 'btn btn-danger', 'confirm' => 'Sure?', 'block' => true]);
29 | echo '
';
30 | }
31 |
32 | ?>
33 |
34 |
35 |
--------------------------------------------------------------------------------
/templates/Admin/Backend/database.php:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
Excluding phinxlog migration tables
13 |
Database size: Number->toReadableSize($dbSize); ?>
14 |
15 |
16 | Name | Rows | Size | Collation | Updated | Comment |
17 | ';
26 | echo '' . h($table['Name']) . ' | ';
27 | echo '' . $table['Rows'] . ' | ';
28 | echo '' . $this->Number->toReadableSize($length) . $this->Progress->htmlProgressBar($size) . ' | ';
29 | echo '' . $table['Engine'] . ' ' . $table['Collation'] . ' | ';
30 | echo '' . $updated . ' | ';
31 | echo '' . h($table['Comment']) . ' | ';
32 | echo '';
33 | }
34 | ?>
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/templates/Admin/Backend/disk_space.php:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
22 |
23 | Free Space
24 | Total Space: Number->toReadableSize($freeSpace['total']);?>
25 |
26 | Number->toReadableSize($freeSpace['available']);?> frei (%),
27 |
28 | Number->toReadableSize($freeSpace['used']);?> belegt (%)
29 |
30 |
31 | Currently used space of project:
32 | Number->toReadableSize($appSize);?>
33 |
34 |
35 |
--------------------------------------------------------------------------------
/templates/Admin/Backend/env.php:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
ENV config
11 |
12 |
13 |
14 | ENV | Value defined |
15 |
16 | $value) { ?>
17 |
18 |
19 |
20 | |
21 |
22 | = $this->element('Setup.ok', ['value' => $this->element('Setup.yes_no', ['value' => $value !== false]), 'ok' => $value !== false, 'escape' => false]) ?>
23 | |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
Dynamic Configs
31 |
32 |
app_local.php
: = $this->element('Setup.yes_no', ['value' => $localConfig !== null]) ?>
33 |
34 |
Defined config keys
35 | loadHelper('Tools.Tree');
38 |
39 | $callback = function ($node) {
40 | if (!$node['hasChildren'] && empty($node['children']) && $node['data']['value'] === []) {
41 | return '';
42 | }
43 |
44 | $name = h($node['data']['name']);
45 | if ($node['hasChildren'] || !empty($node['children'])) {
46 | return $name;
47 | }
48 |
49 | $hasValue = $node['data']['value'] !== null;
50 | $value = '';
51 | if ($hasValue) {
52 | if (!is_string($node['data']['value']) || $node['data']['value'] === '') {
53 | $value = \Cake\Error\Debugger::exportVar($node['data']['value'], 1);
54 | } else {
55 | $value = '(string)...';
56 | }
57 | }
58 |
59 | return $name . ' ' . ($hasValue ? $this->element('Setup.yes_no', ['value' => $hasValue]) . '
' . $value .'
' : '
null
');
60 | };
61 |
62 | echo $this->Tree->generate($localConfig, ['callback' => $callback]);
63 | } else {
64 | echo '
n/a';
65 | }
66 | ?>
67 |
68 |
--------------------------------------------------------------------------------
/templates/Admin/Backend/ip.php:
--------------------------------------------------------------------------------
1 |
2 | $proxyHeaders
8 | */
9 | ?>
10 |
11 |
12 |
IP Address
13 |
14 |
Your Address
15 |
16 |
17 |
18 | IP
19 | |
20 |
21 |
22 | |
23 |
24 |
25 |
26 | Host
27 | |
28 |
29 |
30 | |
31 |
32 |
33 |
34 |
35 |
Proxy Headers found
36 |
37 |
38 | ENV | Value defined |
39 |
40 | $value) { ?>
41 |
42 |
43 |
44 | |
45 |
46 | = \Cake\Error\Debugger::exportVar($value, 1); ?>
47 | |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/templates/Admin/Backend/locales.php:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
Test Locale
14 | Form->create();?>
15 |
23 |
24 |
30 |
31 |
32 | Form->submit(__('Submit')); echo $this->Form->end();?>
33 |
34 |
System Locales
35 |
36 |
37 | = count($systemLocales); ?> locales
38 |
39 |
40 |
41 |
42 |
43 |
Tryouts
44 |
45 | $settings) {
47 | echo '- ';
48 | echo '' . $key . '' . '
' . $settings['res'];
49 | echo pre($settings);
50 | echo '
';
51 | }
52 | ?>
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/templates/Admin/Backend/phpinfo.php:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/templates/Admin/Backend/session.php:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
Session
14 |
15 | Time: Time->niceDate($time); ?>
16 |
17 |
18 |
19 |
Session Config
20 |
23 |
24 |
Cookie Params
25 |
28 |
29 |
Own Session Value
30 |
31 |
ID:
32 |
33 |
Expires: Time->niceDate($sessionData['expires']); ?>
34 |
35 |
36 |
Data:
37 |
38 |
39 |
40 |
Server Timeout
41 | Time->timeAgoInWords((new DateTime())->addSeconds($currentTimeoutInSecs), []);
45 |
46 | ?>
47 |
48 |
49 |
Garbage Collector Settings
50 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/templates/Admin/Backend/system.php:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
Configuration
13 |
14 |
System info
15 |
16 |
17 | Upload-Limit |
18 | Number->toReadableSize($uploadLimit); ?> |
19 |
20 |
21 | Post-Limit |
22 | Number->toReadableSize($postLimit); ?> |
23 |
24 |
25 | Memory Limit |
26 | Number->toReadableSize($memoryLimit); ?> |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/templates/Admin/Backend/timezones.php:
--------------------------------------------------------------------------------
1 |
2 | |null $tokenRaw
9 | * @var string|null $dateTimeString
10 | */
11 |
12 | ?>
13 |
14 |
15 |
Timezones
16 |
17 |
Current
18 |
19 | -
20 | Time: Time->nice($time); ?> ()
21 |
22 | -
23 | Default string cast:
24 |
25 |
26 |
27 |
28 |
Database
29 |
30 |
String in DB:
31 |
32 |
33 | -
34 | nice(): Time->nice($token->created); ?> (created->timezone->getName()); ?>)
35 |
36 | -
37 | format(): Time->format($token->created); ?> (created->timezone->getName()); ?>)
38 |
39 | -
40 | Tools.niceDate(): Time->niceDate($token->created); ?> (created->timezone->getName()); ?>)
41 |
42 |
43 |
44 |
45 | Form->create($token);?>
46 |
52 |
53 |
59 |
60 |
61 | Form->submit(__('Submit')); echo $this->Form->end();?>
62 |
63 | Add Tools plugin to see how forms interact with timezone config.';
65 | } ?>
66 |
67 |
68 |
--------------------------------------------------------------------------------
/templates/Admin/Backend/type_map.php:
--------------------------------------------------------------------------------
1 | $plugins
5 | * @var array $classes
6 | * @var array> $map
7 | */
8 | ?>
9 |
10 | TypeMap overview
11 |
12 |
13 |
14 |
Mapped types
15 |
16 |
17 |
18 | Type |
19 | Name |
20 | Class |
21 |
22 |
23 | $info) {
25 | ?>
26 |
27 |
28 |
29 | |
30 |
31 |
32 | |
33 |
34 |
35 | |
36 |
37 |
40 |
41 |
42 |
43 |
Available classes
44 |
The following request->getQuery('all') ? '' : 'loaded'); ?> plugins have been searched:
45 |
46 |
47 |
48 |
49 |
The following not (yet) used type classes have been found:
50 |
51 | $classNames) { ?>
52 | -
53 |
54 |
55 | $className) { ?>
56 | -
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
request->getQuery('all') ? $this->Html->link('Check loaded plugins only', ['?' => []]) : $this->Html->link('Check all available plugins', ['?' => ['all' => true]])); ?>
69 |
70 |
71 |
--------------------------------------------------------------------------------
/templates/Admin/Setup/index.php:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
Setup Backend Tools
9 |
10 |
Information
11 |
12 | - Html->link('Configuration', ['controller' => 'Configuration', 'action' => 'index']);?>
13 | - Html->link('PHP Info (Full)', ['controller' => 'Backend', 'action' => 'phpinfo']);?>
14 | - Html->link('Session Info', ['controller' => 'Backend', 'action' => 'session']);?>
15 | - Html->link('Cookie Info', ['controller' => 'Backend', 'action' => 'cookies']);?>
16 | - Html->link('Cache Info and Testing', ['controller' => 'Backend', 'action' => 'cache']);?>
17 | - Html->link('ORM Type Map', ['controller' => 'Backend', 'action' => 'typeMap']); ?>
18 |
19 | - Html->link(__('System'), ['controller' => 'Backend', 'action' => 'system']); ?>
20 | - Html->link(__('Timezones'), ['controller' => 'Backend', 'action' => 'timezones']);?>
21 | - Html->link(__('Locales'), ['controller' => 'Backend', 'action' => 'locales']);?>
22 | - Html->link('Database Info', ['controller' => 'Backend', 'action' => 'database']);?>
23 | - Html->link(__('Disk Space'), ['controller' => 'Backend', 'action' => 'diskSpace']);?>
24 | - Html->link(__('ENV Config'), ['controller' => 'Backend', 'action' => 'env']);?>
25 |
26 |
27 |
28 |
Maintenance
29 |
30 | Html->link('Maintenance Mode', ['action' => 'maintenance']);
32 | ?>
33 |
34 |
35 |
Healthcheck
36 |
37 |
38 | Html->link('Healthcheck', ['controller' => 'Uptime', 'action' => 'index']);
40 | ?>
41 |
42 |
You can customize this route on project level and add this to your healthcheck (ping) services.
43 |
44 |
45 |
--------------------------------------------------------------------------------
/templates/Admin/Setup/maintenance.php:
--------------------------------------------------------------------------------
1 | $whitelist
7 | * @var string $ip
8 | */
9 | ?>
10 |
11 |
12 |
Setup Backend Tools
13 |
14 |
Maintenance Mode
15 |
From here you can put your application into maintenance mode if needed.
16 |
17 |
Current Status
18 |
19 |
20 | - Your IP:
21 | - You are currently
22 |
23 |
24 |
25 | Form->postLink('Go to Maintenance mode', ['action' => 'maintenance', '?' => ['maintenance' => 1]], ['class' => 'btn btn-danger']);
28 | } else {
29 | echo $this->Form->postLink('Leave Maintenance mode', ['action' => 'maintenance', '?' => ['maintenance' => 0]], ['class' => 'btn btn-warning']);
30 | }
31 | ?>
32 |
33 |
34 |
Your IP will automatically be whitelisted. So you can still browse.
35 |
36 |
37 |
Whitelist
38 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/templates/Healthcheck/index.php:
--------------------------------------------------------------------------------
1 | $domains
7 | */
8 |
9 | // Make sure to noindex,nofollow this page
10 |
11 | use Cake\Utility\Inflector;
12 | ?>
13 |
14 |
15 | element('Setup.yes_no', ['value' => $passed])?>
16 |
17 |
18 |
19 |
20 |
21 | - Filter:
22 |
23 | request->getQuery('domain')) { ?>
24 | - Html->link('ALL', ['?' => ['domain' => null]]); ?>
25 |
26 |
27 |
28 | - Html->link($domain, ['?' => ['domain' => $domain]]); ?>
29 |
30 |
31 |
32 |
33 | Result
34 | $checks
38 | */
39 | foreach ($result as $domain => $checks) {
40 | ?>
41 |
42 |
43 |
44 |
45 | -
46 | name()); ?>
47 | passed()) { ?>
48 | failureMessage()) { ?>
49 |
failureMessage())); ?>
50 |
51 | warningMessage()) { ?>
52 | warningMessage())); ?>
53 |
54 |
55 | successMessage()) { ?>
56 | successMessage())); ?>
57 |
58 |
59 | infoMessage()) { ?>
60 |
61 | Info
62 |
63 | infoMessage() as $key => $value) { ?>
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | No checks found.
78 |
79 |
80 |
88 |
--------------------------------------------------------------------------------
/templates/bake/Command/command.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 1.7.4
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 | _io`.
27 | *
28 | * @param array $args The arguments for the helper.
29 | * @return void
30 | */
31 | public function output(array $args): void
32 | {
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/templates/bake/Controller/component.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 |
30 | */
31 | protected array $_defaultConfig = [];
32 | }
33 |
--------------------------------------------------------------------------------
/templates/bake/Controller/controller.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * Controller bake template file
4 | *
5 | * Allows templating of Controllers generated from bake.
6 | *
7 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
8 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
9 | *
10 | * Licensed under The MIT License
11 | * For full copyright and license information, please see the LICENSE.txt
12 | * Redistributions of files must retain the above copyright notice.
13 | *
14 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
15 | * @link https://cakephp.org CakePHP(tm) Project
16 | * @since 2.0.0
17 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
18 | */
19 | #}
20 | {{ element('Bake.file_header', {
21 | namespace: "#{namespace}\\Controller#{prefix}",
22 | classImports: (plugin or prefix) ? ["#{baseNamespace}\\Controller\\AppController"] : [],
23 | }) }}
24 |
25 | /**
26 | {% if defaultModel %}
27 | * @property \{{ defaultModel }} ${{ name }}
28 | {% endif %}
29 |
30 | {%- for component in components %}
31 | {% set classInfo = Bake.classInfo(component, 'Controller/Component', 'Component') %}
32 | * @property {{ classInfo.fqn }} ${{ classInfo.name }}
33 | {% endfor %}
34 |
35 | {%- if 'index' in actions %}
36 | * @method \{{ namespace }}\Model\Entity\{{ entityClassName }}[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
37 | {% endif %}
38 | */
39 | class {{ name }}Controller extends AppController
40 | {
41 | {% set pagination = SetupBake.pagination(currentModelName) %}
42 | {% if pagination %}
43 | /**
44 | * @var array
45 | */
46 | protected array $paginate = {{ Bake.exportArray(pagination, 1)|raw }};
47 |
48 | {% endif %}
49 | {% if components or helpers %}
50 | /**
51 | * Initialize controller
52 | *
53 | * @return void
54 | */
55 | public function initialize(): void
56 | {
57 | parent::initialize();
58 |
59 | {% for component in components %}
60 | $this->loadComponent('{{ component }}');
61 | {% endfor %}
62 | {% if helpers %}
63 | $this->viewBuilder()->setHelpers({{ Bake.exportArray(helpers)|raw }});
64 | {% endif %}
65 | }
66 | {% if actions|length %}{{ "\n" }}{% endif %}
67 | {% endif %}
68 | {%- for action in actions %}
69 | {% if loop.index > 1 %}{{ "\n" }}{% endif %}
70 | {{- element('Bake.Controller/' ~ action) -}}
71 | {% endfor %}
72 | }
73 |
--------------------------------------------------------------------------------
/templates/bake/Form/form.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 | handle($request);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/templates/bake/Model/behavior.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 |
30 | */
31 | protected array $_defaultConfig = [];
32 | }
33 |
--------------------------------------------------------------------------------
/templates/bake/Model/entity.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 | {% set propertyHintMap = DocBlock.buildEntityPropertyHintTypeMap(propertySchema ?: []) %}
17 | {% set associationHintMap = DocBlock.buildEntityAssociationHintTypeMap(propertySchema ?: []) %}
18 | {% set annotations = DocBlock.propertyHints(propertyHintMap) %}
19 |
20 | {%- if associationHintMap %}
21 | {%- set annotations = annotations|merge(['']) %}
22 | {%- set annotations = annotations|merge(DocBlock.propertyHints(associationHintMap)) %}
23 | {% endif %}
24 |
25 | {%- set accessible = SetupBake.getFieldAccessibility(fields, primaryKey) %}
26 |
27 | {%- set generatedProperties = [] %}
28 | {{ element('Bake.file_header', {
29 | namespace: fileBuilder.namespace,
30 | classImports: fileBuilder.classImports(['Cake\\ORM\\Entity']),
31 | }) }}
32 |
33 | {{ DocBlock.classDescription(name, 'Entity', annotations)|raw }}
34 | class {{ name }} extends Entity{{ fileBuilder.classBuilder.implements ? ' implements ' ~ fileBuilder.classBuilder.implements|join(', ') : '' }}
35 | {
36 | {% set userConstants = fileBuilder.classBuilder.userConstants([]) %}
37 | {% if userConstants %}
38 | {{~ Bake.concat('\n\n', userConstants) }}
39 |
40 | {% endif %}
41 | {% if accessible %}
42 | {%- set generatedProperties = generatedProperties|merge(['_accessible']) %}
43 | /**
44 | * Fields that can be mass assigned using newEntity() or patchEntity().
45 | *
46 | * Note that when '*' is set to true, this allows all unspecified fields to
47 | * be mass assigned. For security purposes, it is advised to set '*' to false
48 | * (or remove it), and explicitly make individual fields accessible as needed.
49 | *
50 | * @var array
51 | */
52 | protected array $_accessible = {{ Bake.exportVar(accessible, 1)|raw }};
53 | {% endif %}
54 | {% if accessible and hidden %}
55 |
56 | {% endif %}
57 | {%- if hidden %}
58 | {%- set generatedProperties = generatedProperties|merge(['_hidden']) %}
59 | /**
60 | * Fields that are excluded from JSON versions of the entity.
61 | *
62 | * @var list
63 | */
64 | protected array $_hidden = {{ Bake.exportVar(hidden, 1)|raw }};
65 | {% endif %}
66 | {% set userProperties = fileBuilder.classBuilder.userProperties(generatedProperties) %}
67 | {% if userProperties %}
68 |
69 | {{~ Bake.concat('\n\n', userProperties) }}
70 | {% endif %}
71 | {% set userFunctions = fileBuilder.classBuilder.userFunctions([]) %}
72 | {% if userFunctions %}
73 |
74 | {{~ Bake.concat('\n\n', userFunctions) }}
75 | {% endif %}
76 | }
77 |
--------------------------------------------------------------------------------
/templates/bake/Model/enum.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 3.1.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 | {{ element('Bake.file_header', {
17 | namespace: "#{namespace}\\Model\\Enum",
18 | classImports: [
19 | 'Cake\\Database\\Type\\EnumLabelInterface',
20 | 'Cake\\Utility\\Inflector',
21 | ],
22 | }) }}
23 |
24 | {{ DocBlock.classDescription(name, 'Enum', [])|raw }}
25 | enum {{ name }}: {{ backingType }} implements EnumLabelInterface
26 | {
27 | {% if cases %}
28 | {{ Bake.concat('\n ', cases) }}
29 |
30 | {% endif %}
31 | /**
32 | * @return string
33 | */
34 | public function label(): string
35 | {
36 | return Inflector::humanize(Inflector::underscore($this->name));
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/templates/bake/Plugin/.editorconfig.twig:
--------------------------------------------------------------------------------
1 | ; This file is for unifying the coding style for different editors and IDEs.
2 | ; More information at https://editorconfig.org
3 | root = true
4 |
5 | [*]
6 | indent_style = tab
7 | indent_size = 4
8 | end_of_line = lf
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.bat]
13 | end_of_line = crlf
14 |
15 | [*.yml]
16 | indent_style = space
17 | indent_size = 2
18 |
--------------------------------------------------------------------------------
/templates/bake/Plugin/.gitattributes.twig:
--------------------------------------------------------------------------------
1 | # Define the line ending behavior of the different file extensions
2 | # Set default behaviour, in case users don't have core.autocrlf set.
3 | * text ext=auto eol=lf
4 |
5 | *.png binary
6 | *.jpg binary
7 |
8 | # Exclude files for archives generated using `git archive` so that they are not
9 | # included in dist installs through composer.
10 | .editorconfig export-ignore
11 | .gitattributes export-ignore
12 | .gitignore export-ignore
13 | phpunit.xml.dist export-ignore
14 | tests/test_app export-ignore
15 |
--------------------------------------------------------------------------------
/templates/bake/Plugin/.gitignore.twig:
--------------------------------------------------------------------------------
1 | /composer.lock
2 | /composer.phar
3 | /phpunit.xml
4 | /.phpunit.result.cache
5 | /.phpunit.cache/
6 | /phpunit.phar
7 | /config/Migrations/schema-dump-default.lock
8 | /vendor/
9 | /.idea/
10 |
--------------------------------------------------------------------------------
/templates/bake/Plugin/README.md.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 | # {{ plugin }} plugin for CakePHP
17 |
18 | ## Installation
19 |
20 | You can install this plugin into your CakePHP application using [composer](https://getcomposer.org).
21 |
22 | The recommended way to install composer packages is:
23 |
24 | ```
25 | composer require {{ package }}
26 | ```
27 |
--------------------------------------------------------------------------------
/templates/bake/Plugin/composer.json.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 | {% set namespace = namespace|replace({'\\': '\\\\'}) %}
17 | {
18 | "name": "{{ package }}",
19 | "description": "{{ plugin }} plugin for CakePHP",
20 | "type": "cakephp-plugin",
21 | "license": "MIT",
22 | "require": {
23 | "php": ">=8.1",
24 | "cakephp/cakephp": "{{ cakeVersion|raw }}"
25 | },
26 | "require-dev": {
27 | "phpunit/phpunit": "^10.1"
28 | },
29 | "autoload": {
30 | "psr-4": {
31 | "{{ namespace }}\\": "src/"
32 | }
33 | },
34 | "autoload-dev": {
35 | "psr-4": {
36 | "{{ namespace }}\\Test\\": "tests/",
37 | "Cake\\Test\\": "vendor/cakephp/cakephp/tests/"
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/templates/bake/Plugin/phpunit.xml.dist.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | tests/TestCase/
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | src/
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/templates/bake/Plugin/src/Controller/AppController.php.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 |
35 | {{ element('Bake.form') }}
--------------------------------------------------------------------------------
/templates/bake/Template/edit.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 |
35 | {{ element('Bake.form') }}
--------------------------------------------------------------------------------
/templates/bake/Template/login.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 |
21 |
22 | = $this->Form->create() ?>
23 |
28 | = $this->Form->button(__('Login')); ?>
29 | = $this->Form->end() ?>
30 |
31 |
--------------------------------------------------------------------------------
/templates/bake/View/cell.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 | {{ element('Bake.file_header', {
17 | namespace: "#{namespace}\\View\\Cell#{prefix}",
18 | classImports: [
19 | 'Cake\\View\\Cell',
20 | ],
21 | }) }}
22 |
23 | class {{ name }}Cell extends Cell
24 | {
25 | /**
26 | * List of valid options that can be passed into this
27 | * cell's constructor.
28 | *
29 | * @var array
30 | */
31 | protected array $_validCellOptions = [];
32 |
33 | /**
34 | * Initialization logic run at the end of object construction.
35 | *
36 | * @return void
37 | */
38 | public function initialize(): void
39 | {
40 | }
41 |
42 | /**
43 | * Default display method.
44 | *
45 | * @return void
46 | */
47 | public function display()
48 | {
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/templates/bake/View/helper.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 |
30 | */
31 | protected array $_defaultConfig = [];
32 | }
33 |
--------------------------------------------------------------------------------
/templates/bake/element/Controller/add.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 | {% set compact = ["'#{singularName}'"] %}
17 | /**
18 | * @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise.
19 | */
20 | public function add()
21 | {
22 | ${{ singularName }} = $this->{{ currentModelName }}->newEmptyEntity();
23 | if ($this->request->is('post')) {
24 | ${{ singularName }} = $this->{{ currentModelName }}->patchEntity(${{ singularName }}, $this->request->getData());
25 | if ($this->{{ currentModelName }}->save(${{ singularName }})) {
26 | $this->Flash->success(__('The {{ singularHumanName|lower }} has been saved.'));
27 |
28 | return $this->redirect(['action' => 'view', ${{ singularName }}->id]);
29 | }
30 | $this->Flash->error(__('The {{ singularHumanName|lower }} could not be saved. Please, try again.'));
31 | }
32 | {% set associations = Bake.aliasExtractor(modelObj, 'BelongsTo') %}
33 | {% set associations = associations|merge(Bake.aliasExtractor(modelObj, 'BelongsToMany')) %}
34 |
35 | {%- for assoc in associations %}
36 | {%- set otherName = Bake.getAssociatedTableAlias(modelObj, assoc) %}
37 | {%- set otherPlural = otherName|variable %}
38 | ${{ otherPlural }} = $this->{{ currentModelName }}->{{ otherName }}->find('list', limit: 1000)->all();
39 | {{- "\n" }}
40 | {%- set compact = compact|merge(["'#{otherPlural}'"]) %}
41 | {% endfor %}
42 | $this->set(compact({{ compact|join(', ')|raw }}));
43 | }
44 |
--------------------------------------------------------------------------------
/templates/bake/element/Controller/delete.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 | /**
17 | * @param string|null $id {{ singularHumanName }} id.
18 | * @return \Cake\Http\Response|null Redirects to index.
19 | */
20 | public function delete($id = null)
21 | {
22 | $this->request->allowMethod(['post', 'delete']);
23 | ${{ singularName }} = $this->{{ currentModelName }}->get($id);
24 | if ($this->{{ currentModelName }}->delete(${{ singularName }})) {
25 | $this->Flash->success(__('The {{ singularHumanName|lower }} has been deleted.'));
26 | } else {
27 | $this->Flash->error(__('The {{ singularHumanName|lower }} could not be deleted. Please, try again.'));
28 | }
29 |
30 | return $this->redirect(['action' => 'index']);
31 | }
32 |
--------------------------------------------------------------------------------
/templates/bake/element/Controller/edit.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 | {% set belongsTo = Bake.aliasExtractor(modelObj, 'BelongsTo') %}
17 | {% set belongsToMany = Bake.aliasExtractor(modelObj, 'belongsToMany') %}
18 | {% set compact = ["'#{singularName}'"] %}
19 | /**
20 | * @param string|null $id {{ singularHumanName }} id.
21 | * @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise.
22 | */
23 | public function edit($id = null)
24 | {
25 | ${{ singularName }} = $this->{{ currentModelName }}->get($id, contain: {{ Bake.exportArray(belongsToMany)|raw }});
26 | if ($this->request->is(['patch', 'post', 'put'])) {
27 | ${{ singularName }} = $this->{{ currentModelName }}->patchEntity(${{ singularName }}, $this->request->getData());
28 | if ($this->{{ currentModelName }}->save(${{ singularName }})) {
29 | $this->Flash->success(__('The {{ singularHumanName|lower }} has been saved.'));
30 |
31 | return $this->redirect(['action' => 'view', ${{ singularName }}->id]);
32 | }
33 | $this->Flash->error(__('The {{ singularHumanName|lower }} could not be saved. Please, try again.'));
34 | }
35 | {% for assoc in belongsTo|merge(belongsToMany) %}
36 | {%- set otherName = Bake.getAssociatedTableAlias(modelObj, assoc) %}
37 | {%- set otherPlural = otherName|variable %}
38 | ${{ otherPlural }} = $this->{{ currentModelName }}->{{ otherName }}->find('list', limit: 1000)->all();
39 | {{- "\n" }}
40 | {%- set compact = compact|merge(["'#{otherPlural}'"]) %}
41 | {% endfor %}
42 | $this->set(compact({{ compact|join(', ')|raw }}));
43 | }
44 |
--------------------------------------------------------------------------------
/templates/bake/element/Controller/index.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 | /**
17 | * @return \Cake\Http\Response|null|void Renders view
18 | */
19 | public function index()
20 | {
21 | {% set belongsTo = Bake.aliasExtractor(modelObj, 'BelongsTo') %}
22 | {% if belongsTo %}
23 | $query = $this->{{ currentModelName }}->find()
24 | ->contain({{ Bake.exportArray(belongsTo)|raw }});
25 | {% else %}
26 | $query = $this->{{ currentModelName }}->find();
27 | {% endif %}
28 | ${{ pluralName }} = $this->paginate($query);
29 |
30 | $this->set(compact('{{ pluralName }}'));
31 | }
32 |
--------------------------------------------------------------------------------
/templates/bake/element/Controller/login.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 | /**
17 | * @return \Cake\Http\Response|null|void Renders view
18 | */
19 | public function login()
20 | {
21 | {% if Bake.hasPlugin('Authorization') %}
22 | $this->Authorization->skipAuthorization();
23 |
24 | {% endif %}
25 | $result = $this->Authentication->getResult();
26 | if ($result->isValid()) {
27 | $this->Flash->success(__('Login successful'));
28 | $redirect = $this->Authentication->getLoginRedirect();
29 | if ($redirect) {
30 | return $this->redirect($redirect);
31 | }
32 | }
33 |
34 | if ($this->request->is('post')) {
35 | $this->Flash->error(__('Invalid credentials'));
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/templates/bake/element/Controller/logout.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 | /**
17 | * @return \Cake\Http\Response|null
18 | */
19 | public function logout()
20 | {
21 | $whereTo = $this->Authentication->logout() ?: ['action' => 'login'];
22 |
23 | return $this->redirect($whereTo);
24 | }
25 |
--------------------------------------------------------------------------------
/templates/bake/element/Controller/view.twig:
--------------------------------------------------------------------------------
1 | {#
2 | /**
3 | * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4 | * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5 | *
6 | * Licensed under The MIT License
7 | * For full copyright and license information, please see the LICENSE.txt
8 | * Redistributions of files must retain the above copyright notice.
9 | *
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11 | * @link https://cakephp.org CakePHP(tm) Project
12 | * @since 2.0.0
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | #}
16 | {% set allAssociations = Bake.aliasExtractor(modelObj, 'BelongsTo') %}
17 | {% set allAssociations = allAssociations|merge(Bake.aliasExtractor(modelObj, 'BelongsToMany')) %}
18 | {% set allAssociations = allAssociations|merge(Bake.aliasExtractor(modelObj, 'HasOne')) %}
19 | {% set allAssociations = allAssociations|merge(Bake.aliasExtractor(modelObj, 'HasMany')) %}
20 | /**
21 | * @param string|null $id {{ singularHumanName }} id.
22 | * @return \Cake\Http\Response|null|void Renders view
23 | */
24 | public function view($id = null)
25 | {
26 | ${{ singularName }} = $this->{{ currentModelName }}->get($id, contain: {{ Bake.exportArray(allAssociations)|raw }});
27 | $this->set(compact('{{ singularName }}'));
28 | }
29 |
--------------------------------------------------------------------------------
/templates/bake/element/array_property.twig:
--------------------------------------------------------------------------------
1 | /**
2 | * {{ name|humanize }}
3 | *
4 | * @var array
5 | */
6 | public array ${{ name }} = {{ Bake.exportArray(value)|raw }};
--------------------------------------------------------------------------------
/templates/bake/element/file_header.twig:
--------------------------------------------------------------------------------
1 |
46 | */
47 | public array $import = {{ import|raw }};
48 |
49 | {% endif %}
50 |
51 | {%- if schema %}
52 | /**
53 | * Fields
54 | *
55 | * @var array
56 | */
57 | // phpcs:disable
58 | public array $fields = {{ schema|raw }};
59 | // phpcs:enable
60 | {% endif %}
61 |
62 | {%- if records %}
63 | /**
64 | * Init method
65 | *
66 | * @return void
67 | */
68 | public function init(): void
69 | {
70 | $this->records = {{ SetupBake.fixtureRecords(records)|raw }};
71 | parent::init();
72 | }
73 | {% endif %}
74 | }
75 |
--------------------------------------------------------------------------------
/templates/element/l10n_panel.php:
--------------------------------------------------------------------------------
1 | $values
5 | * @var array $timezone
6 | * @var array $currency
7 | * @var array $messages
8 | */
9 | ?>
10 |
11 |
12 | Localization
13 |
14 | Timezone
15 |
16 | - Default:
17 |
18 | - Output:
19 |
20 | - Current:
21 |
22 |
23 | Datetime/Date/Time
24 |
25 |
26 | $value) { ?>
27 | -
28 | : ()
29 |
30 |
31 |
32 |
33 | Languages/Locales
34 |
35 | -
36 | App.defaultLocale:
37 |
38 | -
39 | ini_get('intl.default_locale'):
40 |
41 | -
42 |
43 |
44 |
45 |
46 | Currency
47 |
48 | $value) { ?>
49 | -
50 | :
51 |
52 |
53 |
54 |
55 | Translations
56 | translations
57 |
58 | Details
59 |
60 | $details) { ?>
61 |
62 | |
63 | $translation) {
66 | echo '- ' . ($key ? h($key). ': ' : '') . h($translation). '
';
67 | }
68 | ?> |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/templates/element/ok.php:
--------------------------------------------------------------------------------
1 |
14 | helpers()->has('Templating')) {
16 | echo $this->Templating->ok($value, $ok, ['escape' => $escape]);
17 | } elseif ($this->helpers()->has('Format')) {
18 | echo $this->Format->ok($value, $ok);
19 | } else {
20 | echo $ok ? '' . ($escape ? h($value) : $value) . '' : '' . ($escape ? h($value) : $value) . '';
21 | }
22 | ?>
23 |
--------------------------------------------------------------------------------
/templates/element/yes_no.php:
--------------------------------------------------------------------------------
1 |
9 | helpers()->has('IconSnippet')) {
11 | echo $this->IconSnippet->yesNo($value);
12 | } elseif ($this->helpers()->has('Format')) {
13 | echo $this->Format->yesNo($value);
14 | } else {
15 | echo $value ? 'Yes' : 'No';
16 | }
17 | ?>
18 |
--------------------------------------------------------------------------------
/tests/Fixture/QueuedJobsFixture.php:
--------------------------------------------------------------------------------
1 | ['type' => 'integer', 'length' => 11, 'unsigned' => false, 'null' => false, 'default' => null, 'comment' => '', 'autoIncrement' => true, 'precision' => null],
22 | 'job_task' => ['type' => 'string', 'length' => 90, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null],
23 | 'data' => ['type' => 'text', 'length' => 16777215, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null],
24 | 'job_group' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null],
25 | 'reference' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null],
26 | 'created' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null],
27 | 'notbefore' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null],
28 | 'fetched' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null],
29 | 'completed' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null],
30 | 'progress' => ['type' => 'float', 'length' => null, 'precision' => null, 'unsigned' => false, 'null' => true, 'default' => null, 'comment' => ''],
31 | 'attempts' => ['type' => 'integer', 'length' => 12, 'unsigned' => false, 'null' => false, 'default' => 0, 'comment' => '', 'precision' => null, 'autoIncrement' => null],
32 | 'failure_message' => ['type' => 'text', 'length' => 16777215, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null],
33 | 'workerkey' => ['type' => 'string', 'length' => 45, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null],
34 | 'status' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null],
35 | 'priority' => ['type' => 'integer', 'length' => 3, 'unsigned' => false, 'null' => false, 'default' => 5, 'comment' => '', 'precision' => null, 'autoIncrement' => null],
36 | '_constraints' => [
37 | 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []],
38 | ],
39 | '_options' => [
40 | 'engine' => 'InnoDB',
41 | ],
42 | ];
43 | // @codingStandardsIgnoreEnd
44 |
45 | /**
46 | * Init method
47 | *
48 | * @return void
49 | */
50 | public function init(): void {
51 | $this->records = [
52 | ];
53 | parent::init();
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/tests/Fixture/SessionsFixture.php:
--------------------------------------------------------------------------------
1 | ['type' => 'string', 'length' => 128],
20 | 'data' => ['type' => 'binary', 'length' => TableSchema::LENGTH_MEDIUM, 'null' => true],
21 | 'expires' => ['type' => 'integer', 'length' => 11, 'null' => true],
22 | '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]],
23 | ];
24 |
25 | /**
26 | * records property
27 | *
28 | * @var array
29 | */
30 | public array $records = [];
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/tests/Fixture/UsersFixture.php:
--------------------------------------------------------------------------------
1 | ['type' => 'integer'],
19 | 'username' => ['type' => 'string', 'null' => true],
20 | 'email' => ['type' => 'string', 'null' => true],
21 | 'password' => ['type' => 'string', 'null' => true],
22 | 'created' => ['type' => 'timestamp', 'null' => true],
23 | 'updated' => ['type' => 'timestamp', 'null' => true],
24 | '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]],
25 | ];
26 |
27 | /**
28 | * records property
29 | *
30 | * @var array
31 | */
32 | public array $records = [
33 | [
34 | 'username' => 'mariano',
35 | 'password' => '$2a$10$u05j8FjsvLBNdfhBhc21LOuVMpzpabVXQ9OpC2wO3pSO0q6t7HHMO',
36 | 'email' => 'example@example.org',
37 | 'created' => '2007-03-17 01:16:23',
38 | 'updated' => '2007-03-17 01:18:31',
39 | ],
40 | ];
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/tests/TestCase/Command/CliTestCommandTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
21 | }
22 |
23 | /**
24 | * @return void
25 | */
26 | public function testBasic() {
27 | $this->skipIf(version_compare(Configure::version(), '5.1', '<'));
28 |
29 | $this->exec('cli_test');
30 | $this->assertOutputContains('Router::url([\'controller\' => \'Test\'], true)');
31 | $this->assertOutputContains('/test');
32 | }
33 |
34 | /**
35 | * @return void
36 | */
37 | public function testPrefix() {
38 | $builder = Router::createRouteBuilder('/');
39 | $builder->setRouteClass(DashedRoute::class);
40 | $builder->scope('/', function (RouteBuilder $routes): void {
41 | $routes->fallbacks();
42 | });
43 | $builder->prefix('Admin', function (RouteBuilder $routes): void {
44 | $routes->fallbacks();
45 | });
46 |
47 | $this->exec('cli_test --prefix Admin');
48 |
49 | $this->assertOutputContains('/admin/test');
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/tests/TestCase/Command/CurrentConfigCommandTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
20 | }
21 |
22 | /**
23 | * @return void
24 | */
25 | public function testConfigure() {
26 | $this->exec('current_config configure');
27 | $this->assertOutputContains('[App]');
28 | $this->assertOutputContains('[namespace] =>');
29 | }
30 |
31 | /**
32 | * @return void
33 | */
34 | public function testDisplay() {
35 | $this->exec('current_config display');
36 | $this->assertOutputContains('Full Base URL:');
37 | }
38 |
39 | /**
40 | * @return void
41 | */
42 | public function testPhpinfo() {
43 | $this->exec('current_config phpinfo');
44 | $this->assertOutputContains('session.auto_start');
45 | }
46 |
47 | /**
48 | * @return void
49 | */
50 | public function testValidate() {
51 | $this->exec('current_config validate');
52 | $this->assertOutputContains('[driver]');
53 | $this->assertOutputContains('"className"');
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/tests/TestCase/Command/DbInitCommandTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
22 | }
23 |
24 | /**
25 | * @return void
26 | */
27 | public function testInit() {
28 | $this->skipIfNotDriver('Sqlite');
29 |
30 | $this->exec('db init');
31 |
32 | $this->assertErrorContains('Using in-memory database, skipping');
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/tests/TestCase/Command/DbResetCommandTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
20 | }
21 |
22 | /**
23 | * @return void
24 | */
25 | public function testReset() {
26 | $this->exec('db reset --dry-run');
27 |
28 | $this->assertOutputContains('DRY-RUN');
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/tests/TestCase/Command/DbWipeCommandTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
20 | }
21 |
22 | /**
23 | * @return void
24 | */
25 | public function testReset() {
26 | $this->exec('db wipe --dry-run');
27 |
28 | $this->assertOutputContains('DRY-RUN');
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/tests/TestCase/Command/HealthcheckCommandTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
25 | }
26 |
27 | /**
28 | * Test defaultName method
29 | *
30 | * @return void
31 | */
32 | public function testExecute(): void {
33 | Configure::write('Setup.Healthcheck.checks', [
34 | PhpVersionCheck::class,
35 | ]);
36 |
37 | Configure::write('App.fullBaseUrl', 'https://example.com');
38 |
39 | $this->exec('healthcheck -v');
40 |
41 | $this->assertExitSuccess();
42 | $this->assertOutputContains('=> OK');
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/tests/TestCase/Command/MaintenanceModeActivateCommandTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
17 | }
18 |
19 | /**
20 | * @return void
21 | */
22 | public function testActivate() {
23 | $this->exec('maintenance_mode activate');
24 | $this->assertOutputContains('Maintenance mode activated ...');
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/tests/TestCase/Command/MaintenanceModeDeactivateCommandTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
17 | }
18 |
19 | /**
20 | * @return void
21 | */
22 | public function testDeactivate() {
23 | $this->exec('maintenance_mode deactivate');
24 | $this->assertOutputContains('Maintenance mode deactivated ...');
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/tests/TestCase/Command/MaintenanceModeStatusCommandTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
19 |
20 | if (file_exists(TMP . 'maintenance.txt')) {
21 | unlink(TMP . 'maintenance.txt');
22 | }
23 | }
24 |
25 | /**
26 | * @return void
27 | */
28 | public function testStatus() {
29 | $this->exec('maintenance_mode status');
30 | $this->assertOutputContains('Maintenance mode not active');
31 | }
32 |
33 | /**
34 | * @return void
35 | */
36 | public function testStatusActive() {
37 | $this->exec('maintenance_mode activate');
38 |
39 | $this->exec('maintenance_mode status');
40 | $this->assertOutputContains('Maintenance mode active');
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/tests/TestCase/Command/MaintenanceModeWhitelistCommandTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
19 |
20 | $matches = glob(TMP . 'maintenanceOverride-*\.txt');
21 | if ($matches) {
22 | foreach ($matches as $match) {
23 | unlink($match);
24 | }
25 | }
26 | }
27 |
28 | /**
29 | * @return void
30 | */
31 | public function testWhitelist() {
32 | $this->exec('maintenance_mode whitelist');
33 | $this->assertOutputContains('n/a');
34 |
35 | $this->exec('maintenance_mode whitelist 192.168.0.1');
36 | $this->assertOutputContains('192.168.0.1');
37 |
38 | $this->exec('maintenance_mode whitelist -r');
39 | $this->assertOutputContains('n/a');
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/tests/TestCase/Command/ResetCommandTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
20 | }
21 |
22 | /**
23 | * @return void
24 | */
25 | public function testUpdate() {
26 | $this->exec('reset pwd', ['123']);
27 |
28 | $this->assertOutputContains('0 pwds resetted - DONE');
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/tests/TestCase/Command/UserCreateCommandTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
20 | }
21 |
22 | /**
23 | * @return void
24 | */
25 | public function testCreate() {
26 | $this->exec('user create admin 123', ['y', 'some@email.de', 'y']);
27 | $this->assertOutputContains('User inserted! ID: 1');
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/tests/TestCase/Command/UserUpdateCommandTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
20 | }
21 |
22 | /**
23 | * @return void
24 | */
25 | public function testUpdate() {
26 | $this->exec('user create admin 123', ['y', 'some@email.de', 'y']);
27 |
28 | $this->exec('user update admin 123456', []);
29 | $this->assertOutputContains('Password updated for user admin');
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/tests/TestCase/Controller/Admin/ConfigurationControllerTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testIndex() {
25 | $this->disableErrorHandlerMiddleware();
26 |
27 | $this->session(['Auth' => ['User' => ['id' => 1, 'role_id' => 9]]]);
28 |
29 | $this->get(['prefix' => 'Admin', 'plugin' => 'Setup', 'controller' => 'Configuration', 'action' => 'index']);
30 |
31 | $this->assertResponseCode(200);
32 | $this->assertNoRedirect();
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/tests/TestCase/Controller/Admin/DatabaseControllerTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
21 | }
22 |
23 | /**
24 | * @return void
25 | */
26 | public function testForeignKeys() {
27 | $this->skipIfNotDriver('Mysql', 'Only for MYSQL for now');
28 |
29 | $this->disableErrorHandlerMiddleware();
30 |
31 | $this->session(['Auth' => ['User' => ['id' => 1]]]);
32 |
33 | $this->get(['prefix' => 'Admin', 'plugin' => 'Setup', 'controller' => 'Database', 'action' => 'foreignKeys']);
34 |
35 | $this->assertResponseCode(200);
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/tests/TestCase/Controller/Admin/SetupControllerTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
21 | }
22 |
23 | /**
24 | * @return void
25 | */
26 | public function testIndex() {
27 | $this->disableErrorHandlerMiddleware();
28 |
29 | $this->session(['Auth' => ['User' => ['id' => 1]]]);
30 |
31 | $this->get(['prefix' => 'Admin', 'plugin' => 'Setup', 'controller' => 'Setup', 'action' => 'index']);
32 |
33 | $this->assertResponseCode(200);
34 | }
35 |
36 | /**
37 | * @return void
38 | */
39 | public function testMaintenance() {
40 | $this->disableErrorHandlerMiddleware();
41 |
42 | $this->session(['Auth' => ['User' => ['id' => 1]]]);
43 |
44 | $this->get(['prefix' => 'Admin', 'plugin' => 'Setup', 'controller' => 'Setup', 'action' => 'maintenance']);
45 |
46 | $this->assertResponseCode(200);
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/tests/TestCase/Controller/HealthcheckControllerTest.php:
--------------------------------------------------------------------------------
1 | loadPlugins(['Setup']);
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testIndex() {
25 | $this->disableErrorHandlerMiddleware();
26 |
27 | $this->get(['plugin' => 'Setup', 'controller' => 'Healthcheck', 'action' => 'index']);
28 |
29 | $this->assertResponseCode(200);
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/tests/TestCase/Healthcheck/Check/Core/CakeCacheCheckTest.php:
--------------------------------------------------------------------------------
1 | assertSame('Core', $check->domain());
17 | }
18 |
19 | /**
20 | * @return void
21 | */
22 | public function testCheck() {
23 | $check = new CakeCacheCheck();
24 |
25 | $check->check();
26 | $this->assertTrue($check->passed(), print_r($check->__debugInfo(), true));
27 | }
28 |
29 | /**
30 | * @return void
31 | */
32 | public function testCheckTooLow() {
33 | Configure::write('Healthcheck.checkCacheKeys', ['xyz']);
34 |
35 | $check = new CakeCacheCheck();
36 | $check->check();
37 | $this->assertFalse($check->passed(), print_r($check->__debugInfo(), true));
38 |
39 | $this->assertSame(['The following cache setups are missing: xyz.'], $check->failureMessage());
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/tests/TestCase/Healthcheck/Check/Core/CakeSaltCheckTest.php:
--------------------------------------------------------------------------------
1 | assertSame('Core', $check->domain());
17 | }
18 |
19 | /**
20 | * @return void
21 | */
22 | public function testCheck() {
23 | Configure::write('Security.salt', '123456');
24 |
25 | $check = new CakeSaltCheck();
26 |
27 | $check->check();
28 | $this->assertTrue($check->passed(), print_r($check->__debugInfo(), true));
29 | }
30 |
31 | /**
32 | * @return void
33 | */
34 | public function testCheckTooLow() {
35 | Configure::write('Security.salt', '__SALT__');
36 |
37 | $check = new CakeSaltCheck();
38 | $check->check();
39 | $this->assertFalse($check->passed(), print_r($check->__debugInfo(), true));
40 |
41 | $this->assertNotEmpty($check->failureMessage());
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/tests/TestCase/Healthcheck/Check/Core/CakeVersionCheckTest.php:
--------------------------------------------------------------------------------
1 | testFiles = ROOT . DS . 'tests' . DS . 'test_files' . DS . 'healthcheck' . DS;
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testDomain(): void {
25 | $check = new CakeVersionCheck();
26 | $this->assertSame('Core', $check->domain());
27 | }
28 |
29 | /**
30 | * @return void
31 | */
32 | public function testCheck() {
33 | $check = new CakeVersionCheck('5.2.1', $this->testFiles . 'PhpVersionCheck' . DS . 'basic' . DS);
34 |
35 | $check->check();
36 | $this->assertTrue($check->passed(), print_r($check->__debugInfo(), true));
37 | }
38 |
39 | /**
40 | * @return void
41 | */
42 | public function testCheckTooLow() {
43 | $check = new CakeVersionCheck('5.2.0', $this->testFiles . 'PhpVersionCheck' . DS . 'basic' . DS);
44 |
45 | $check->check();
46 | $this->assertFalse($check->passed(), print_r($check->__debugInfo(), true));
47 |
48 | $this->assertNotEmpty($check->failureMessage());
49 | }
50 |
51 | /**
52 | * @return void
53 | */
54 | public function testCheckTooHigh() {
55 | $check = new CakeVersionCheck('5.3.0', $this->testFiles . 'PhpVersionCheck' . DS . 'basic' . DS);
56 |
57 | $check->check();
58 | $this->assertFalse($check->passed(), print_r($check->__debugInfo(), true));
59 |
60 | $this->assertNotEmpty($check->failureMessage());
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/tests/TestCase/Healthcheck/Check/Core/FullBaseUrlCheckTest.php:
--------------------------------------------------------------------------------
1 | assertSame('Core', $check->domain());
17 | }
18 |
19 | /**
20 | * @return void
21 | */
22 | public function testCheck() {
23 | $check = new FullBaseUrlCheck();
24 |
25 | Configure::write('App.fullBaseUrl', 'https://example.com');
26 |
27 | $check->check();
28 | $this->assertTrue($check->passed(), print_r($check->__debugInfo(), true));
29 | }
30 |
31 | /**
32 | * @return void
33 | */
34 | public function testCheckMissing() {
35 | $check = new FullBaseUrlCheck();
36 |
37 | Configure::write('App.fullBaseUrl', '');
38 |
39 | $check->check();
40 | $this->assertFalse($check->passed(), print_r($check->__debugInfo(), true));
41 |
42 | $this->assertNotEmpty($check->failureMessage());
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/tests/TestCase/Healthcheck/Check/Database/ConnectCheckTest.php:
--------------------------------------------------------------------------------
1 | assertSame('Database', $check->domain());
16 | }
17 |
18 | /**
19 | * @return void
20 | */
21 | public function testCheck() {
22 | $check = new ConnectCheck();
23 |
24 | $check->check();
25 | $this->assertTrue($check->passed(), print_r($check->__debugInfo(), true));
26 | }
27 |
28 | /**
29 | * @return void
30 | */
31 | public function testCheckInvalid() {
32 | $check = new ConnectCheck('foo');
33 |
34 | $check->check();
35 | $this->assertFalse($check->passed(), print_r($check->__debugInfo(), true));
36 |
37 | $this->assertSame(['Cannot connect to database on connection `foo`: The datasource configuration `foo` was not found.'], $check->failureMessage());
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/tests/TestCase/Healthcheck/Check/Environment/PhpUploadLimitCheckTest.php:
--------------------------------------------------------------------------------
1 | assertSame('Environment', $check->domain());
16 | }
17 |
18 | /**
19 | * @return void
20 | */
21 | public function testCheck() {
22 | $check = new PhpUploadLimitCheck(2);
23 |
24 | $check->check();
25 | $this->assertTrue($check->passed(), print_r($check->__debugInfo(), true));
26 | }
27 |
28 | /**
29 | * @return void
30 | */
31 | public function testCheckTooLow() {
32 | $check = new PhpUploadLimitCheck(2000);
33 |
34 | $check->check();
35 | $this->assertFalse($check->passed(), print_r($check->__debugInfo(), true));
36 |
37 | $this->assertNotEmpty($check->failureMessage());
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/tests/TestCase/Healthcheck/Check/Environment/PhpVersionCheckTest.php:
--------------------------------------------------------------------------------
1 | testFiles = ROOT . DS . 'tests' . DS . 'test_files' . DS . 'healthcheck' . DS;
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testDomain(): void {
25 | $check = new PhpVersionCheck();
26 | $this->assertSame('Environment', $check->domain());
27 | }
28 |
29 | /**
30 | * @return void
31 | */
32 | public function testCheck() {
33 | $check = new PhpVersionCheck('8.3.10', $this->testFiles . 'PhpVersionCheck' . DS . 'basic' . DS);
34 |
35 | $check->check();
36 | $this->assertTrue($check->passed(), print_r($check->__debugInfo(), true));
37 | }
38 |
39 | /**
40 | * @return void
41 | */
42 | public function testCheckTooLow() {
43 | $check = new PhpVersionCheck('8.3.0', $this->testFiles . 'PhpVersionCheck' . DS . 'basic' . DS);
44 |
45 | $check->check();
46 | $this->assertFalse($check->passed(), print_r($check->__debugInfo(), true));
47 |
48 | $this->assertNotEmpty($check->failureMessage());
49 | }
50 |
51 | /**
52 | * @return void
53 | */
54 | public function testCheckTooHigh() {
55 | $check = new PhpVersionCheck('8.4.0', $this->testFiles . 'PhpVersionCheck' . DS . 'basic' . DS);
56 |
57 | $check->check();
58 | $this->assertFalse($check->passed(), print_r($check->__debugInfo(), true));
59 |
60 | $this->assertNotEmpty($check->failureMessage());
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/tests/TestCase/Healthcheck/Check/HealthcheckCollectorTest.php:
--------------------------------------------------------------------------------
1 | assertNotEmpty($checks);
18 | foreach ($checks as $check => $options) {
19 | $this->assertTrue(is_subclass_of($check, CheckInterface::class), $check . ' actually is ' . gettype($check));
20 | $this->assertSame([], $options, print_r($options, true));
21 | }
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/tests/TestCase/Maintenance/MaintenanceTest.php:
--------------------------------------------------------------------------------
1 | Maintenance = new Maintenance();
22 |
23 | if (file_exists(TMP . 'maintenance.txt')) {
24 | unlink(TMP . 'maintenance.txt');
25 | }
26 | }
27 |
28 | /**
29 | * @return void
30 | */
31 | protected function tearDown(): void {
32 | parent::tearDown();
33 |
34 | $this->Maintenance->setMaintenanceMode(false);
35 | $this->Maintenance->clearWhitelist();
36 | }
37 |
38 | /**
39 | * MaintenanceLibTest::testStatus()
40 | *
41 | * @return void
42 | */
43 | public function testStatus() {
44 | $status = $this->Maintenance->isMaintenanceMode();
45 | $this->assertFalse($status);
46 |
47 | $this->Maintenance->setMaintenanceMode(true);
48 | $status = $this->Maintenance->isMaintenanceMode();
49 | $this->assertTrue($status);
50 |
51 | $this->assertFileExists(TMP . 'maintenance.txt');
52 | }
53 |
54 | /**
55 | * @return void
56 | */
57 | public function testWhitelist() {
58 | $result = $this->Maintenance->whitelist();
59 | $this->assertEmpty($result);
60 |
61 | $whitelist = ['192.168.0.1'];
62 | $this->Maintenance->addToWhitelist($whitelist);
63 |
64 | $result = $this->Maintenance->whitelist();
65 | $this->assertNotSame([], $result);
66 |
67 | $this->Maintenance->clearWhitelist(['192.111.111.111']);
68 | $result = $this->Maintenance->whitelist();
69 |
70 | $this->assertSame($whitelist, $result);
71 |
72 | $this->Maintenance->clearWhitelist();
73 | $result = $this->Maintenance->whitelist();
74 | $this->assertSame([], $result);
75 | }
76 |
77 | /**
78 | * @return void
79 | */
80 | public function testWhitelistSubnet() {
81 | $result = $this->Maintenance->whitelist();
82 | $this->assertEmpty($result);
83 |
84 | $whitelist = ['5.146.197.0/24'];
85 | $this->Maintenance->addToWhitelist($whitelist);
86 |
87 | $result = $this->Maintenance->whitelist();
88 | $this->assertNotEmpty($result);
89 |
90 | $result = $this->Maintenance->whitelisted('5.146.197.255');
91 | $this->assertTrue($result);
92 |
93 | $this->Maintenance->clearWhitelist(['5.146.197.0/24']);
94 | $result = $this->Maintenance->whitelist();
95 | $this->assertSame([], $result);
96 |
97 | $this->Maintenance->clearWhitelist();
98 | $result = $this->Maintenance->whitelist();
99 | $this->assertEmpty($result);
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/tests/TestCase/Middleware/MaintenanceMiddlewareTest.php:
--------------------------------------------------------------------------------
1 | invokeMethod($middleware, 'build', [$response]);
28 |
29 | $this->assertStringContainsString('Please wait... We will be back shortly', $result->getBody()->getContents());
30 |
31 | $this->assertSame(503, $result->getStatusCode());
32 | }
33 |
34 | /**
35 | * @return void
36 | */
37 | public function testBuildCustomLayout() {
38 | $config = [
39 | 'templateLayout' => 'maintenance',
40 | ];
41 |
42 | $middleware = new MaintenanceMiddleware($config);
43 |
44 | $response = new Response();
45 | /** @var \Cake\Http\Response $result */
46 | $result = $this->invokeMethod($middleware, 'build', [$response]);
47 |
48 | $body = $result->getBody()->getContents();
49 | $this->assertStringContainsString('Please wait... We will be back shortly', $body);
50 | $this->assertStringContainsString('We are working right now
', $body);
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/tests/TestCase/Queue/Task/HealthcheckTaskTest.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | protected array $fixtures = [
14 | ];
15 |
16 | /**
17 | * @return void
18 | */
19 | #[\PHPUnit\Framework\Attributes\DoesNotPerformAssertions]
20 | public function testRun(): void {
21 | $task = new HealthcheckTask();
22 |
23 | $task->run([], 0);
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/tests/TestCase/Utility/SetupTest.php:
--------------------------------------------------------------------------------
1 | 'ControllerName', 'action' => 'action_name', '?' => ['clearcache' => 1, 'foo' => 'bar']];
15 | $result = Setup::cleanedUrl('clearcache', $url);
16 | $expected = ['controller' => 'ControllerName', 'action' => 'action_name', '?' => ['foo' => 'bar']];
17 | $this->assertSame($expected, $result);
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/tests/config/bootstrap.php:
--------------------------------------------------------------------------------
1 | setRouteClass(DashedRoute::class);
9 |
10 | $routes->scope('/', function (RouteBuilder $routes) {
11 | $routes->fallbacks();
12 | });
13 | $routes->prefix('Admin', function (RouteBuilder $routes) {
14 | $routes->fallbacks();
15 | });
16 | };
17 |
--------------------------------------------------------------------------------
/tests/schema.php:
--------------------------------------------------------------------------------
1 | $iterator
9 | */
10 | $iterator = new DirectoryIterator(__DIR__ . DS . 'Fixture');
11 | foreach ($iterator as $file) {
12 | if (!preg_match('/(\w+)Fixture.php$/', (string)$file, $matches)) {
13 | continue;
14 | }
15 |
16 | $name = $matches[1];
17 | $tableName = null;
18 | $class = 'Setup\\Test\\Fixture\\' . $name . 'Fixture';
19 | try {
20 | $fieldsObject = (new ReflectionClass($class))->getProperty('fields');
21 | $tableObject = (new ReflectionClass($class))->getProperty('table');
22 | $tableName = $tableObject->getDefaultValue();
23 |
24 | } catch (ReflectionException $e) {
25 | continue;
26 | }
27 |
28 | if (!$tableName) {
29 | $tableName = Inflector::underscore($name);
30 | }
31 |
32 | $array = $fieldsObject->getDefaultValue();
33 | $constraints = $array['_constraints'] ?? [];
34 | $indexes = $array['_indexes'] ?? [];
35 | unset($array['_constraints'], $array['_indexes'], $array['_options']);
36 | $table = [
37 | 'table' => $tableName,
38 | 'columns' => $array,
39 | 'constraints' => $constraints,
40 | 'indexes' => $indexes,
41 | ];
42 | $tables[$tableName] = $table;
43 | }
44 |
45 | return $tables;
46 |
--------------------------------------------------------------------------------
/tests/shim.php:
--------------------------------------------------------------------------------
1 |