Initial commit: CakePHP project
Some checks failed
Mark stale issues and pull requests / stale (push) Has been cancelled
Some checks failed
Mark stale issues and pull requests / stale (push) Has been cancelled
This commit is contained in:
163
src/Application.php
Normal file
163
src/Application.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.3.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace App;
|
||||
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Core\ContainerInterface;
|
||||
use Cake\Datasource\FactoryLocator;
|
||||
use Cake\Error\Middleware\ErrorHandlerMiddleware;
|
||||
use Cake\Http\BaseApplication;
|
||||
use Cake\Http\Middleware\BodyParserMiddleware;
|
||||
use Cake\Http\Middleware\CsrfProtectionMiddleware;
|
||||
use Cake\Http\MiddlewareQueue;
|
||||
use Cake\ORM\Locator\TableLocator;
|
||||
use Cake\Routing\Middleware\AssetMiddleware;
|
||||
use Cake\Routing\Middleware\RoutingMiddleware;
|
||||
|
||||
// dodaj
|
||||
use Authentication\AuthenticationService;
|
||||
use Authentication\AuthenticationServiceInterface;
|
||||
use Authentication\AuthenticationServiceProviderInterface;
|
||||
use Authentication\Middleware\AuthenticationMiddleware;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Authentication\Identifier\AbstractIdentifier;
|
||||
use Cake\Routing\Router;
|
||||
|
||||
/**
|
||||
* Application setup class.
|
||||
*
|
||||
* This defines the bootstrapping logic and middleware layers you
|
||||
* want to use in your application.
|
||||
*
|
||||
* @extends \Cake\Http\BaseApplication<\App\Application>
|
||||
*/
|
||||
class Application extends BaseApplication
|
||||
implements AuthenticationServiceProviderInterface
|
||||
{
|
||||
/**
|
||||
* Load all the application configuration and bootstrap logic.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(): void
|
||||
{
|
||||
// Call parent to load bootstrap from files.
|
||||
parent::bootstrap();
|
||||
$this->addPlugin('Authentication'); // zaladuj
|
||||
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
// The bake plugin requires fallback table classes to work properly
|
||||
FactoryLocator::add('Table', (new TableLocator())->allowFallbackClass(false));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the middleware queue your application will use.
|
||||
*
|
||||
* @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to setup.
|
||||
* @return \Cake\Http\MiddlewareQueue The updated middleware queue.
|
||||
*/
|
||||
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
|
||||
{
|
||||
$middlewareQueue
|
||||
// Catch any exceptions in the lower layers,
|
||||
// and make an error page/response
|
||||
->add(new ErrorHandlerMiddleware(Configure::read('Error'), $this))
|
||||
|
||||
// Handle plugin/theme assets like CakePHP normally does.
|
||||
->add(new AssetMiddleware([
|
||||
'cacheTime' => Configure::read('Asset.cacheTime'),
|
||||
]))
|
||||
|
||||
// Add routing middleware.
|
||||
// If you have a large number of routes connected, turning on routes
|
||||
// caching in production could improve performance.
|
||||
// See https://github.com/CakeDC/cakephp-cached-routing
|
||||
->add(new RoutingMiddleware($this))
|
||||
|
||||
// Parse various types of encoded request bodies so that they are
|
||||
// available as array through $request->getData()
|
||||
// https://book.cakephp.org/5/en/controllers/middleware.html#body-parser-middleware
|
||||
->add(new BodyParserMiddleware())
|
||||
|
||||
// Cross Site Request Forgery (CSRF) Protection Middleware
|
||||
// https://book.cakephp.org/5/en/security/csrf.html#cross-site-request-forgery-csrf-middleware
|
||||
->add(new CsrfProtectionMiddleware([
|
||||
'httponly' => true,
|
||||
]))
|
||||
|
||||
// Dodaj AuthenticationMiddleware
|
||||
// za routing i body parser.
|
||||
->add(new AuthenticationMiddleware($this));
|
||||
|
||||
return $middlewareQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register application container services.
|
||||
*
|
||||
* @param \Cake\Core\ContainerInterface $container The Container to update.
|
||||
* @return void
|
||||
* @link https://book.cakephp.org/5/en/development/dependency-injection.html#dependency-injection
|
||||
*/
|
||||
public function services(ContainerInterface $container): void
|
||||
{
|
||||
}
|
||||
|
||||
// Dodaj metode
|
||||
public function getAuthenticationService(ServerRequestInterface $request):
|
||||
AuthenticationServiceInterface
|
||||
{
|
||||
$service = new AuthenticationService();
|
||||
$service->setConfig([
|
||||
'unauthenticatedRedirect' => [
|
||||
'prefix' => false,
|
||||
'plugin' => false,
|
||||
'controller' => 'Users',
|
||||
'action' => 'login',
|
||||
],
|
||||
'queryParam' => 'redirect',
|
||||
]);
|
||||
|
||||
// login i haslo
|
||||
$fields = [
|
||||
AbstractIdentifier::CREDENTIAL_USERNAME => 'email',
|
||||
AbstractIdentifier::CREDENTIAL_PASSWORD => 'password'
|
||||
];
|
||||
$passwordIdentifier = [
|
||||
'Authentication.Password' => [
|
||||
'fields' => $fields,
|
||||
],
|
||||
];
|
||||
|
||||
$service->loadAuthenticator('Authentication.Session', [
|
||||
'identifier' => $passwordIdentifier,
|
||||
]);
|
||||
$service->loadAuthenticator('Authentication.Form', [
|
||||
'identifier' => $passwordIdentifier,
|
||||
'fields' => $fields,
|
||||
'loginUrl' => Router::url([
|
||||
'prefix' => false,
|
||||
'plugin' => null,
|
||||
'controller' => 'Users',
|
||||
'action' => 'login',
|
||||
]),
|
||||
]);
|
||||
|
||||
return $service;
|
||||
}
|
||||
}
|
||||
260
src/Console/Installer.php
Normal file
260
src/Console/Installer.php
Normal file
@@ -0,0 +1,260 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace App\Console;
|
||||
|
||||
if (!defined('STDIN')) {
|
||||
define('STDIN', fopen('php://stdin', 'r'));
|
||||
}
|
||||
|
||||
use Cake\Codeception\Console\Installer as CodeceptionInstaller;
|
||||
use Cake\Utility\Security;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Script\Event;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Provides installation hooks for when this application is installed through
|
||||
* composer. Customize this class to suit your needs.
|
||||
*/
|
||||
class Installer
|
||||
{
|
||||
/**
|
||||
* An array of directories to be made writable
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
public const WRITABLE_DIRS = [
|
||||
'logs',
|
||||
'tmp',
|
||||
'tmp/cache',
|
||||
'tmp/cache/models',
|
||||
'tmp/cache/persistent',
|
||||
'tmp/cache/views',
|
||||
'tmp/sessions',
|
||||
'tmp/tests',
|
||||
];
|
||||
|
||||
/**
|
||||
* Does some routine installation tasks so people don't have to.
|
||||
*
|
||||
* @param \Composer\Script\Event $event The composer event object.
|
||||
* @throws \Exception Exception raised by validator.
|
||||
* @return void
|
||||
*/
|
||||
public static function postInstall(Event $event): void
|
||||
{
|
||||
$io = $event->getIO();
|
||||
|
||||
$rootDir = dirname(__DIR__, 2);
|
||||
|
||||
static::createAppLocalConfig($rootDir, $io);
|
||||
static::createWritableDirectories($rootDir, $io);
|
||||
|
||||
static::setFolderPermissions($rootDir, $io);
|
||||
static::setSecuritySalt($rootDir, $io);
|
||||
|
||||
if (class_exists(CodeceptionInstaller::class)) {
|
||||
CodeceptionInstaller::customizeCodeceptionBinary($event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create config/app_local.php file if it does not exist.
|
||||
*
|
||||
* @param string $dir The application's root directory.
|
||||
* @param \Composer\IO\IOInterface $io IO interface to write to console.
|
||||
* @return void
|
||||
*/
|
||||
public static function createAppLocalConfig(string $dir, IOInterface $io): void
|
||||
{
|
||||
$appLocalConfig = $dir . '/config/app_local.php';
|
||||
$appLocalConfigTemplate = $dir . '/config/app_local.example.php';
|
||||
if (!file_exists($appLocalConfig)) {
|
||||
copy($appLocalConfigTemplate, $appLocalConfig);
|
||||
$io->write('Created `config/app_local.php` file');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the `logs` and `tmp` directories.
|
||||
*
|
||||
* @param string $dir The application's root directory.
|
||||
* @param \Composer\IO\IOInterface $io IO interface to write to console.
|
||||
* @return void
|
||||
*/
|
||||
public static function createWritableDirectories(string $dir, IOInterface $io): void
|
||||
{
|
||||
foreach (static::WRITABLE_DIRS as $path) {
|
||||
$path = $dir . '/' . $path;
|
||||
if (!file_exists($path)) {
|
||||
mkdir($path);
|
||||
$io->write('Created `' . $path . '` directory');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set globally writable permissions on the "tmp" and "logs" directory.
|
||||
*
|
||||
* This is not the most secure default, but it gets people up and running quickly.
|
||||
*
|
||||
* @param string $dir The application's root directory.
|
||||
* @param \Composer\IO\IOInterface $io IO interface to write to console.
|
||||
* @return void
|
||||
*/
|
||||
public static function setFolderPermissions(string $dir, IOInterface $io): void
|
||||
{
|
||||
// ask if the permissions should be changed
|
||||
if ($io->isInteractive()) {
|
||||
$validator = function (string $arg): string {
|
||||
if (in_array($arg, ['Y', 'y', 'N', 'n'])) {
|
||||
return $arg;
|
||||
}
|
||||
throw new Exception('This is not a valid answer. Please choose Y or n.');
|
||||
};
|
||||
$setFolderPermissions = $io->askAndValidate(
|
||||
'<info>Set Folder Permissions ? (Default to Y)</info> [<comment>Y,n</comment>]? ',
|
||||
$validator,
|
||||
10,
|
||||
'Y',
|
||||
);
|
||||
|
||||
if (in_array($setFolderPermissions, ['n', 'N'])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Change the permissions on a path and output the results.
|
||||
$changePerms = function (string $path) use ($io): void {
|
||||
$currentPerms = fileperms($path) & 0777;
|
||||
$worldWritable = $currentPerms | 0007;
|
||||
if ($worldWritable == $currentPerms) {
|
||||
return;
|
||||
}
|
||||
|
||||
$res = chmod($path, $worldWritable);
|
||||
if ($res) {
|
||||
$io->write('Permissions set on ' . $path);
|
||||
} else {
|
||||
$io->write('Failed to set permissions on ' . $path);
|
||||
}
|
||||
};
|
||||
|
||||
$walker = function (string $dir) use (&$walker, $changePerms): void {
|
||||
$files = array_diff(scandir($dir) ?: [], ['.', '..']);
|
||||
foreach ($files as $file) {
|
||||
$path = $dir . '/' . $file;
|
||||
|
||||
if (!is_dir($path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$changePerms($path);
|
||||
$walker($path);
|
||||
}
|
||||
};
|
||||
|
||||
$walker($dir . '/tmp');
|
||||
$changePerms($dir . '/tmp');
|
||||
$changePerms($dir . '/logs');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the security.salt value in the application's config file.
|
||||
*
|
||||
* @param string $dir The application's root directory.
|
||||
* @param \Composer\IO\IOInterface $io IO interface to write to console.
|
||||
* @return void
|
||||
*/
|
||||
public static function setSecuritySalt(string $dir, IOInterface $io): void
|
||||
{
|
||||
$newKey = hash('sha256', Security::randomBytes(64));
|
||||
static::setSecuritySaltInFile($dir, $io, $newKey, 'app_local.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the security.salt value in a given file
|
||||
*
|
||||
* @param string $dir The application's root directory.
|
||||
* @param \Composer\IO\IOInterface $io IO interface to write to console.
|
||||
* @param string $newKey key to set in the file
|
||||
* @param string $file A path to a file relative to the application's root
|
||||
* @return void
|
||||
*/
|
||||
public static function setSecuritySaltInFile(string $dir, IOInterface $io, string $newKey, string $file): void
|
||||
{
|
||||
$config = $dir . '/config/' . $file;
|
||||
$content = file_get_contents($config);
|
||||
if ($content === false) {
|
||||
$io->write('Config file not readable or not found: config/' . $file);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$content = str_replace('__SALT__', $newKey, $content, $count);
|
||||
|
||||
if ($count == 0) {
|
||||
$io->write('No Security.salt placeholder to replace.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$result = file_put_contents($config, $content);
|
||||
if ($result) {
|
||||
$io->write('Updated Security.salt value in config/' . $file);
|
||||
|
||||
return;
|
||||
}
|
||||
$io->write('Unable to update Security.salt value.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the APP_NAME value in a given file
|
||||
*
|
||||
* @param string $dir The application's root directory.
|
||||
* @param \Composer\IO\IOInterface $io IO interface to write to console.
|
||||
* @param string $appName app name to set in the file
|
||||
* @param string $file A path to a file relative to the application's root
|
||||
* @return void
|
||||
*/
|
||||
public static function setAppNameInFile(string $dir, IOInterface $io, string $appName, string $file): void
|
||||
{
|
||||
$config = $dir . '/config/' . $file;
|
||||
$content = file_get_contents($config);
|
||||
if ($content === false) {
|
||||
$io->write('Config file not readable or not found: config/' . $file);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$content = str_replace('__APP_NAME__', $appName, $content, $count);
|
||||
|
||||
if ($count == 0) {
|
||||
$io->write('No __APP_NAME__ placeholder to replace.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$result = file_put_contents($config, $content);
|
||||
if ($result) {
|
||||
$io->write('Updated __APP_NAME__ value in config/' . $file);
|
||||
|
||||
return;
|
||||
}
|
||||
$io->write('Unable to update __APP_NAME__ value.');
|
||||
}
|
||||
}
|
||||
53
src/Controller/AppController.php
Normal file
53
src/Controller/AppController.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.2.9
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace App\Controller;
|
||||
|
||||
use Cake\Controller\Controller;
|
||||
|
||||
/**
|
||||
* Application Controller
|
||||
*
|
||||
* Add your application-wide methods in the class below, your controllers
|
||||
* will inherit them.
|
||||
*
|
||||
* @link https://book.cakephp.org/5/en/controllers.html#the-app-controller
|
||||
*/
|
||||
class AppController extends Controller
|
||||
{
|
||||
/**
|
||||
* Initialization hook method.
|
||||
*
|
||||
* Use this method to add common initialization code like loading components.
|
||||
*
|
||||
* e.g. `$this->loadComponent('FormProtection');`
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(): void
|
||||
{
|
||||
parent::initialize();
|
||||
|
||||
$this->loadComponent('Flash');
|
||||
$this->loadComponent('Authentication.Authentication');
|
||||
|
||||
/*
|
||||
* Enable the following component for recommended CakePHP form protection settings.
|
||||
* see https://book.cakephp.org/5/en/controllers/components/form-protection.html
|
||||
*/
|
||||
//$this->loadComponent('FormProtection');
|
||||
}
|
||||
}
|
||||
0
src/Controller/Component/.gitkeep
Normal file
0
src/Controller/Component/.gitkeep
Normal file
70
src/Controller/ErrorController.php
Normal file
70
src/Controller/ErrorController.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.3.4
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace App\Controller;
|
||||
|
||||
use Cake\Event\EventInterface;
|
||||
|
||||
/**
|
||||
* Error Handling Controller
|
||||
*
|
||||
* Controller used by ExceptionRenderer to render error responses.
|
||||
*/
|
||||
class ErrorController extends AppController
|
||||
{
|
||||
/**
|
||||
* Initialization hook method.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(): void
|
||||
{
|
||||
// Only add parent::initialize() if you are confident your `AppController` is safe.
|
||||
}
|
||||
|
||||
/**
|
||||
* beforeFilter callback.
|
||||
*
|
||||
* @param \Cake\Event\EventInterface<\Cake\Controller\Controller> $event Event.
|
||||
* @return void
|
||||
*/
|
||||
public function beforeFilter(EventInterface $event): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* beforeRender callback.
|
||||
*
|
||||
* @param \Cake\Event\EventInterface<\Cake\Controller\Controller> $event Event.
|
||||
* @return void
|
||||
*/
|
||||
public function beforeRender(EventInterface $event): void
|
||||
{
|
||||
parent::beforeRender($event);
|
||||
|
||||
$this->viewBuilder()->setTemplatePath('Error');
|
||||
}
|
||||
|
||||
/**
|
||||
* afterFilter callback.
|
||||
*
|
||||
* @param \Cake\Event\EventInterface<\Cake\Controller\Controller> $event Event.
|
||||
* @return void
|
||||
*/
|
||||
public function afterFilter(EventInterface $event): void
|
||||
{
|
||||
}
|
||||
}
|
||||
73
src/Controller/PagesController.php
Normal file
73
src/Controller/PagesController.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.2.9
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace App\Controller;
|
||||
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Http\Exception\ForbiddenException;
|
||||
use Cake\Http\Exception\NotFoundException;
|
||||
use Cake\Http\Response;
|
||||
use Cake\View\Exception\MissingTemplateException;
|
||||
|
||||
/**
|
||||
* Static content controller
|
||||
*
|
||||
* This controller will render views from templates/Pages/
|
||||
*
|
||||
* @link https://book.cakephp.org/5/en/controllers/pages-controller.html
|
||||
*/
|
||||
class PagesController extends AppController
|
||||
{
|
||||
/**
|
||||
* Displays a view
|
||||
*
|
||||
* @param string ...$path Path segments.
|
||||
* @return \Cake\Http\Response|null
|
||||
* @throws \Cake\Http\Exception\ForbiddenException When a directory traversal attempt.
|
||||
* @throws \Cake\View\Exception\MissingTemplateException When the view file could not
|
||||
* be found and in debug mode.
|
||||
* @throws \Cake\Http\Exception\NotFoundException When the view file could not
|
||||
* be found and not in debug mode.
|
||||
* @throws \Cake\View\Exception\MissingTemplateException In debug mode.
|
||||
*/
|
||||
public function display(string ...$path): ?Response
|
||||
{
|
||||
if (!$path) {
|
||||
return $this->redirect('/');
|
||||
}
|
||||
if (in_array('..', $path, true) || in_array('.', $path, true)) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
$page = $subpage = null;
|
||||
|
||||
if (!empty($path[0])) {
|
||||
$page = $path[0];
|
||||
}
|
||||
if (!empty($path[1])) {
|
||||
$subpage = $path[1];
|
||||
}
|
||||
$this->set(compact('page', 'subpage'));
|
||||
|
||||
try {
|
||||
return $this->render(implode('/', $path));
|
||||
} catch (MissingTemplateException $exception) {
|
||||
if (Configure::read('debug')) {
|
||||
throw $exception;
|
||||
}
|
||||
throw new NotFoundException();
|
||||
}
|
||||
}
|
||||
}
|
||||
100
src/Controller/ToolsController.php
Normal file
100
src/Controller/ToolsController.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
/**
|
||||
* Tools Controller
|
||||
*
|
||||
* @property \App\Model\Table\ToolsTable $Tools
|
||||
*/
|
||||
class ToolsController extends AppController
|
||||
{
|
||||
/**
|
||||
* Index method
|
||||
*
|
||||
* @return \Cake\Http\Response|null|void Renders view
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$query = $this->Tools->find();
|
||||
$tools = $this->paginate($query);
|
||||
|
||||
$this->set(compact('tools'));
|
||||
}
|
||||
|
||||
/**
|
||||
* View method
|
||||
*
|
||||
* @param string|null $id Tool id.
|
||||
* @return \Cake\Http\Response|null|void Renders view
|
||||
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
|
||||
*/
|
||||
public function view($id = null)
|
||||
{
|
||||
$tool = $this->Tools->get($id, contain: []);
|
||||
$this->set(compact('tool'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add method
|
||||
*
|
||||
* @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise.
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$tool = $this->Tools->newEmptyEntity();
|
||||
if ($this->request->is('post')) {
|
||||
$tool = $this->Tools->patchEntity($tool, $this->request->getData());
|
||||
if ($this->Tools->save($tool)) {
|
||||
$this->Flash->success(__('Dodano poprawnie.'));
|
||||
|
||||
return $this->redirect(['action' => 'index']);
|
||||
}
|
||||
$this->Flash->error(__('Błąd dodawania. Spróbuj ponownie.'));
|
||||
}
|
||||
$this->set(compact('tool'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit method
|
||||
*
|
||||
* @param string|null $id Tool id.
|
||||
* @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise.
|
||||
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
|
||||
*/
|
||||
public function edit($id = null)
|
||||
{
|
||||
$tool = $this->Tools->get($id, contain: []);
|
||||
if ($this->request->is(['patch', 'post', 'put'])) {
|
||||
$tool = $this->Tools->patchEntity($tool, $this->request->getData());
|
||||
if ($this->Tools->save($tool)) {
|
||||
$this->Flash->success(__('Zapisano poprawnie.'));
|
||||
|
||||
return $this->redirect(['action' => 'index']);
|
||||
}
|
||||
$this->Flash->error(__('Błąd edycji. Spróbuj ponownie.'));
|
||||
}
|
||||
$this->set(compact('tool'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete method
|
||||
*
|
||||
* @param string|null $id Tool id.
|
||||
* @return \Cake\Http\Response|null Redirects to index.
|
||||
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
|
||||
*/
|
||||
public function delete($id = null)
|
||||
{
|
||||
$this->request->allowMethod(['post', 'delete']);
|
||||
$tool = $this->Tools->get($id);
|
||||
if ($this->Tools->delete($tool)) {
|
||||
$this->Flash->success(__('Usunięto poprawnie.'));
|
||||
} else {
|
||||
$this->Flash->error(__('Błąd usuwania'));
|
||||
}
|
||||
|
||||
return $this->redirect(['action' => 'index']);
|
||||
}
|
||||
}
|
||||
132
src/Controller/UsersController.php
Normal file
132
src/Controller/UsersController.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
/**
|
||||
* Users Controller
|
||||
*
|
||||
* @property \App\Model\Table\UsersTable $Users
|
||||
*/
|
||||
class UsersController extends AppController
|
||||
{
|
||||
/**
|
||||
* Index method
|
||||
*
|
||||
* @return \Cake\Http\Response|null|void Renders view
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$query = $this->Users->find();
|
||||
$users = $this->paginate($query);
|
||||
|
||||
$this->set(compact('users'));
|
||||
}
|
||||
|
||||
/**
|
||||
* View method
|
||||
*
|
||||
* @param string|null $id User id.
|
||||
* @return \Cake\Http\Response|null|void Renders view
|
||||
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
|
||||
*/
|
||||
public function view($id = null)
|
||||
{
|
||||
$user = $this->Users->get($id, contain: []);
|
||||
$this->set(compact('user'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add method
|
||||
*
|
||||
* @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise.
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$user = $this->Users->newEmptyEntity();
|
||||
if ($this->request->is('post')) {
|
||||
$user = $this->Users->patchEntity($user, $this->request->getData());
|
||||
if ($this->Users->save($user)) {
|
||||
$this->Flash->success(__('The user has been saved.'));
|
||||
|
||||
return $this->redirect(['action' => 'index']);
|
||||
}
|
||||
$this->Flash->error(__('The user could not be saved. Please, try again.'));
|
||||
}
|
||||
$this->set(compact('user'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit method
|
||||
*
|
||||
* @param string|null $id User id.
|
||||
* @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise.
|
||||
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
|
||||
*/
|
||||
public function edit($id = null)
|
||||
{
|
||||
$user = $this->Users->get($id, contain: []);
|
||||
if ($this->request->is(['patch', 'post', 'put'])) {
|
||||
$user = $this->Users->patchEntity($user, $this->request->getData());
|
||||
if ($this->Users->save($user)) {
|
||||
$this->Flash->success(__('The user has been saved.'));
|
||||
|
||||
return $this->redirect(['action' => 'index']);
|
||||
}
|
||||
$this->Flash->error(__('The user could not be saved. Please, try again.'));
|
||||
}
|
||||
$this->set(compact('user'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete method
|
||||
*
|
||||
* @param string|null $id User id.
|
||||
* @return \Cake\Http\Response|null Redirects to index.
|
||||
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
|
||||
*/
|
||||
public function delete($id = null)
|
||||
{
|
||||
$this->request->allowMethod(['post', 'delete']);
|
||||
$user = $this->Users->get($id);
|
||||
if ($this->Users->delete($user)) {
|
||||
$this->Flash->success(__('The user has been deleted.'));
|
||||
} else {
|
||||
$this->Flash->error(__('The user could not be deleted. Please, try again.'));
|
||||
}
|
||||
|
||||
return $this->redirect(['action' => 'index']);
|
||||
}
|
||||
|
||||
public function initialize(): void
|
||||
{
|
||||
parent::initialize();
|
||||
$this->Authentication->allowUnauthenticated(['login']);
|
||||
}
|
||||
|
||||
public function login()
|
||||
{
|
||||
$this->request->allowMethod(['get', 'post']);
|
||||
$result = $this->Authentication->getResult();
|
||||
if ($result->isValid()) {
|
||||
$this->Flash->success(__('Zalogowano poprawnie'));
|
||||
$redirect = $this->Authentication->getLoginRedirect();
|
||||
if ($redirect) {
|
||||
return $this->redirect($redirect);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->request->is('post')) {
|
||||
$this->Flash->error(__('Błędna nazwa użytkownika lub hasło'));
|
||||
}
|
||||
}
|
||||
|
||||
public function logout()
|
||||
{
|
||||
$result = $this->Authentication->getResult();
|
||||
if ($result && $result->isValid()) {
|
||||
$this->Authentication->logout();
|
||||
return $this->redirect(['controller' => 'Users', 'action' => 'login']);
|
||||
}
|
||||
}
|
||||
}
|
||||
0
src/Model/Behavior/.gitkeep
Normal file
0
src/Model/Behavior/.gitkeep
Normal file
38
src/Model/Entity/Tool.php
Normal file
38
src/Model/Entity/Tool.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model\Entity;
|
||||
|
||||
use Cake\ORM\Entity;
|
||||
|
||||
/**
|
||||
* Tool Entity
|
||||
*
|
||||
* @property int $id
|
||||
* @property string $name
|
||||
* @property string|null $description
|
||||
* @property int $quantity
|
||||
* @property bool $active
|
||||
* @property \Cake\I18n\DateTime $created
|
||||
* @property \Cake\I18n\DateTime|null $modified
|
||||
*/
|
||||
class Tool extends Entity
|
||||
{
|
||||
/**
|
||||
* Fields that can be mass assigned using newEntity() or patchEntity().
|
||||
*
|
||||
* Note that when '*' is set to true, this allows all unspecified fields to
|
||||
* be mass assigned. For security purposes, it is advised to set '*' to false
|
||||
* (or remove it), and explicitly make individual fields accessible as needed.
|
||||
*
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
protected array $_accessible = [
|
||||
'name' => true,
|
||||
'description' => true,
|
||||
'quantity' => true,
|
||||
'active' => true,
|
||||
'created' => true,
|
||||
'modified' => true,
|
||||
];
|
||||
}
|
||||
51
src/Model/Entity/User.php
Normal file
51
src/Model/Entity/User.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model\Entity;
|
||||
|
||||
use Cake\ORM\Entity;
|
||||
use Authentication\PasswordHasher\DefaultPasswordHasher;
|
||||
|
||||
/**
|
||||
* User Entity
|
||||
*
|
||||
* @property int $id
|
||||
* @property string $name
|
||||
* @property string $email
|
||||
* @property string $password
|
||||
* @property \Cake\I18n\DateTime $created
|
||||
* @property \Cake\I18n\DateTime|null $modified
|
||||
*/
|
||||
class User extends Entity
|
||||
{
|
||||
/**
|
||||
* Fields that can be mass assigned using newEntity() or patchEntity().
|
||||
*
|
||||
* Note that when '*' is set to true, this allows all unspecified fields to
|
||||
* be mass assigned. For security purposes, it is advised to set '*' to false
|
||||
* (or remove it), and explicitly make individual fields accessible as needed.
|
||||
*
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
protected array $_accessible = [
|
||||
'name' => true,
|
||||
'email' => true,
|
||||
'password' => true,
|
||||
'created' => true,
|
||||
'modified' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* Fields that are excluded from JSON versions of the entity.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected array $_hidden = [
|
||||
'password',
|
||||
];
|
||||
protected function _setPassword(string $password)
|
||||
{
|
||||
$hasher = new DefaultPasswordHasher();
|
||||
return $hasher->hash($password);
|
||||
}
|
||||
}
|
||||
85
src/Model/Table/ToolsTable.php
Normal file
85
src/Model/Table/ToolsTable.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model\Table;
|
||||
|
||||
use Cake\ORM\Query\SelectQuery;
|
||||
use Cake\ORM\RulesChecker;
|
||||
use Cake\ORM\Table;
|
||||
use Cake\Validation\Validator;
|
||||
|
||||
/**
|
||||
* Tools Model
|
||||
*
|
||||
* @method \App\Model\Entity\Tool newEmptyEntity()
|
||||
* @method \App\Model\Entity\Tool newEntity(array $data, array $options = [])
|
||||
* @method array<\App\Model\Entity\Tool> newEntities(array $data, array $options = [])
|
||||
* @method \App\Model\Entity\Tool get(mixed $primaryKey, array|string $finder = 'all', \Psr\SimpleCache\CacheInterface|string|null $cache = null, \Closure|string|null $cacheKey = null, mixed ...$args)
|
||||
* @method \App\Model\Entity\Tool findOrCreate($search, ?callable $callback = null, array $options = [])
|
||||
* @method \App\Model\Entity\Tool patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
|
||||
* @method array<\App\Model\Entity\Tool> patchEntities(iterable $entities, array $data, array $options = [])
|
||||
* @method \App\Model\Entity\Tool|false save(\Cake\Datasource\EntityInterface $entity, array $options = [])
|
||||
* @method \App\Model\Entity\Tool saveOrFail(\Cake\Datasource\EntityInterface $entity, array $options = [])
|
||||
* @method iterable<\App\Model\Entity\Tool>|\Cake\Datasource\ResultSetInterface<\App\Model\Entity\Tool>|false saveMany(iterable $entities, array $options = [])
|
||||
* @method iterable<\App\Model\Entity\Tool>|\Cake\Datasource\ResultSetInterface<\App\Model\Entity\Tool> saveManyOrFail(iterable $entities, array $options = [])
|
||||
* @method iterable<\App\Model\Entity\Tool>|\Cake\Datasource\ResultSetInterface<\App\Model\Entity\Tool>|false deleteMany(iterable $entities, array $options = [])
|
||||
* @method iterable<\App\Model\Entity\Tool>|\Cake\Datasource\ResultSetInterface<\App\Model\Entity\Tool> deleteManyOrFail(iterable $entities, array $options = [])
|
||||
*
|
||||
* @mixin \Cake\ORM\Behavior\TimestampBehavior
|
||||
*/
|
||||
class ToolsTable extends Table
|
||||
{
|
||||
//Metoda initialize() to konfiguracja modelu
|
||||
// setTable oznacza, ze nazwa tabeli to tools.
|
||||
// setDisplayField pole, które reprezentuje tabele w listach.
|
||||
// setPrimaryKey ustawia klucz główny.
|
||||
// addBehavior ustawia pola timestamp
|
||||
/**
|
||||
* Initialize method
|
||||
*
|
||||
* @param array<string, mixed> $config The configuration for the Table.
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(array $config): void
|
||||
{
|
||||
parent::initialize($config);
|
||||
|
||||
$this->setTable('tools');
|
||||
$this->setDisplayField('name');
|
||||
$this->setPrimaryKey('id');
|
||||
|
||||
$this->addBehavior('Timestamp');
|
||||
}
|
||||
|
||||
// Metoda validationDefault to walidacja danych
|
||||
// Walidacja przy tworzeniu nowych encji (create)
|
||||
// przy aktualizacji (patchEntity), przy zapisie (save)
|
||||
/**
|
||||
* Default validation rules.
|
||||
*
|
||||
* @param \Cake\Validation\Validator $validator Validator instance.
|
||||
* @return \Cake\Validation\Validator
|
||||
*/
|
||||
public function validationDefault(Validator $validator): Validator
|
||||
{
|
||||
$validator
|
||||
->scalar('name') // nazwa pola
|
||||
->maxLength('name', 150) // string, maks. 150 znakow,
|
||||
->requirePresence('name', 'create') // podczas tworzenia
|
||||
->notEmptyString('name'); // nie mo˙ ze by´ c puste
|
||||
|
||||
$validator
|
||||
->scalar('description')
|
||||
->allowEmptyString('description'); // mo˙ ze by´ c puste
|
||||
|
||||
$validator
|
||||
->integer('quantity')
|
||||
->notEmptyString('quantity');
|
||||
|
||||
$validator
|
||||
->boolean('active')
|
||||
->notEmptyString('active');
|
||||
|
||||
return $validator;
|
||||
}
|
||||
}
|
||||
90
src/Model/Table/UsersTable.php
Normal file
90
src/Model/Table/UsersTable.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model\Table;
|
||||
|
||||
use Cake\ORM\Query\SelectQuery;
|
||||
use Cake\ORM\RulesChecker;
|
||||
use Cake\ORM\Table;
|
||||
use Cake\Validation\Validator;
|
||||
|
||||
/**
|
||||
* Users Model
|
||||
*
|
||||
* @method \App\Model\Entity\User newEmptyEntity()
|
||||
* @method \App\Model\Entity\User newEntity(array $data, array $options = [])
|
||||
* @method array<\App\Model\Entity\User> newEntities(array $data, array $options = [])
|
||||
* @method \App\Model\Entity\User get(mixed $primaryKey, array|string $finder = 'all', \Psr\SimpleCache\CacheInterface|string|null $cache = null, \Closure|string|null $cacheKey = null, mixed ...$args)
|
||||
* @method \App\Model\Entity\User findOrCreate($search, ?callable $callback = null, array $options = [])
|
||||
* @method \App\Model\Entity\User patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
|
||||
* @method array<\App\Model\Entity\User> patchEntities(iterable $entities, array $data, array $options = [])
|
||||
* @method \App\Model\Entity\User|false save(\Cake\Datasource\EntityInterface $entity, array $options = [])
|
||||
* @method \App\Model\Entity\User saveOrFail(\Cake\Datasource\EntityInterface $entity, array $options = [])
|
||||
* @method iterable<\App\Model\Entity\User>|\Cake\Datasource\ResultSetInterface<\App\Model\Entity\User>|false saveMany(iterable $entities, array $options = [])
|
||||
* @method iterable<\App\Model\Entity\User>|\Cake\Datasource\ResultSetInterface<\App\Model\Entity\User> saveManyOrFail(iterable $entities, array $options = [])
|
||||
* @method iterable<\App\Model\Entity\User>|\Cake\Datasource\ResultSetInterface<\App\Model\Entity\User>|false deleteMany(iterable $entities, array $options = [])
|
||||
* @method iterable<\App\Model\Entity\User>|\Cake\Datasource\ResultSetInterface<\App\Model\Entity\User> deleteManyOrFail(iterable $entities, array $options = [])
|
||||
*
|
||||
* @mixin \Cake\ORM\Behavior\TimestampBehavior
|
||||
*/
|
||||
class UsersTable extends Table
|
||||
{
|
||||
/**
|
||||
* Initialize method
|
||||
*
|
||||
* @param array<string, mixed> $config The configuration for the Table.
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(array $config): void
|
||||
{
|
||||
parent::initialize($config);
|
||||
|
||||
$this->setTable('users');
|
||||
$this->setDisplayField('name');
|
||||
$this->setPrimaryKey('id');
|
||||
|
||||
$this->addBehavior('Timestamp');
|
||||
}
|
||||
|
||||
/**
|
||||
* Default validation rules.
|
||||
*
|
||||
* @param \Cake\Validation\Validator $validator Validator instance.
|
||||
* @return \Cake\Validation\Validator
|
||||
*/
|
||||
public function validationDefault(Validator $validator): Validator
|
||||
{
|
||||
$validator
|
||||
->scalar('name')
|
||||
->maxLength('name', 150)
|
||||
->requirePresence('name', 'create')
|
||||
->notEmptyString('name');
|
||||
|
||||
$validator
|
||||
->email('email')
|
||||
->requirePresence('email', 'create')
|
||||
->notEmptyString('email');
|
||||
|
||||
$validator
|
||||
->scalar('password')
|
||||
->maxLength('password', 255)
|
||||
->requirePresence('password', 'create')
|
||||
->notEmptyString('password');
|
||||
|
||||
return $validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a rules checker object that will be used for validating
|
||||
* application integrity.
|
||||
*
|
||||
* @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
|
||||
* @return \Cake\ORM\RulesChecker
|
||||
*/
|
||||
public function buildRules(RulesChecker $rules): RulesChecker
|
||||
{
|
||||
$rules->add($rules->isUnique(['email']), ['errorField' => 'email']);
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
46
src/View/AjaxView.php
Normal file
46
src/View/AjaxView.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.4
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace App\View;
|
||||
|
||||
/**
|
||||
* A view class that is used for AJAX responses.
|
||||
* Currently only switches the default layout and sets the response type -
|
||||
* which just maps to text/html by default.
|
||||
*/
|
||||
class AjaxView extends AppView
|
||||
{
|
||||
/**
|
||||
* The name of the layout file to render the view inside of. The name
|
||||
* specified is the filename of the layout in /templates/Layout without
|
||||
* the .php extension.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $layout = 'ajax';
|
||||
|
||||
/**
|
||||
* Initialization hook method.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(): void
|
||||
{
|
||||
parent::initialize();
|
||||
|
||||
$this->response = $this->response->withType('ajax');
|
||||
}
|
||||
}
|
||||
41
src/View/AppView.php
Normal file
41
src/View/AppView.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace App\View;
|
||||
|
||||
use Cake\View\View;
|
||||
|
||||
/**
|
||||
* Application View
|
||||
*
|
||||
* Your application's default view class
|
||||
*
|
||||
* @link https://book.cakephp.org/5/en/views.html#the-app-view
|
||||
*/
|
||||
class AppView extends View
|
||||
{
|
||||
/**
|
||||
* Initialization hook method.
|
||||
*
|
||||
* Use this method to add common initialization code like adding helpers.
|
||||
*
|
||||
* e.g. `$this->addHelper('Html');`
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(): void
|
||||
{
|
||||
}
|
||||
}
|
||||
0
src/View/Cell/.gitkeep
Normal file
0
src/View/Cell/.gitkeep
Normal file
0
src/View/Helper/.gitkeep
Normal file
0
src/View/Helper/.gitkeep
Normal file
Reference in New Issue
Block a user