diff --git a/.gitattributes b/.gitattributes index 7748d23b..bd121e8e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,5 +4,6 @@ *.jpeg binary *.svg binary *.md diff=markdown +*.ttf export-ignore *.woff export-ignore *.woff2 export-ignore diff --git a/README.md b/README.md index bed23755..b5390fb2 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ Documentation is available at: https://docs.dotkernel.org/admin-documentation/ ## Tools -Dotkernel can be installed through a single command that utilizes [Composer](https://getcomposer.org/). Because of that, -Composer is required to install Dotkernel `admin`. +Dotkernel can be installed through a single command that utilizes [Composer](https://getcomposer.org/). +Because of that, Composer is required to install Dotkernel Admin. ### Composer @@ -41,24 +41,23 @@ Installation instructions: - [Composer Installation - Linux/Unix/OSX](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx) - [Composer Installation - Windows](https://getcomposer.org/doc/00-intro.md#installation-windows) ->If you have never used composer before make sure you read ->the [`Composer Basic Usage`](https://getcomposer.org/doc/01-basic-usage.md) section in Composer's documentation +> If you have never used composer before make sure you read the [`Composer Basic Usage`](https://getcomposer.org/doc/01-basic-usage.md) section in Composer's documentation. -## Choosing an installation path for Dotkernel `admin` +## Choosing an installation path for Dotkernel Admin Example: - absolute path `/var/www/dk-admin` - or relative path `dk-admin` (equivalent with `./dk-admin`) -## Installing Dotkernel `admin` +## Installing Dotkernel Admin After choosing the path for Dotkernel (`dk-admin` will be used for the remainder of this example) it must be installed. There are two installation methods. -### I. Installing Dotkernel `admin` using composer +### I. Installing Dotkernel Admin using Composer ->please use the below CLI commands in terminal, do NOT use the PhpStorm buttons +> please use the below CLI commands in terminal, do NOT use the PhpStorm buttons The advantage of using this command is that it runs through the whole installation process. Run the following command: @@ -72,23 +71,24 @@ The setup script prompts for some configuration settings, for example the lines ```text Please select which config file you wish to inject 'Laminas\Diactoros\ConfigProvider' into: - [0] Do not inject - [1] config/config.php - Make your selection (default is 1): + [0] Do not inject + [1] config/config.php + Make your selection (default is 1): ``` -Simply select `[0] Do not inject`, because Dotkernel includes its own configProvider which already contains the prompted -configurations. +Simply select `[0] Do not inject`, because Dotkernel includes its own configProvider which already contains the prompted configurations. -If you choose `[1] config/config.php` Laminas's `ConfigProvider` from `session` will be injected. +If you choose `[1] config/config.php` Laminas' `ConfigProvider` from `session` will be injected. The next question is: -`Remember this option for other packages of the same type? (y/N)` +```shell +Remember this option for other packages of the same type? (y/N) +``` -Type `y` here, and hit `enter` +Type `y` here, and hit `Enter`. -### II. Installing Dotkernel `admin` using git clone +### II. Installing Dotkernel Admin using git clone This method requires more manual input, but it ensures that the default branch is installed, even if it is not released. Run the following command: @@ -103,58 +103,58 @@ The dependencies have to be installed separately, by running this command composer install ``` -Just like for `II Installing Dotkernel admin using composer` (see above), the setup asks for configuration settings -regarding injections (type `0` and hit `enter`) and a confirmation to use this setting for other packages (type `y` and -hit `enter`) +Just like when [Installing Dotkernel admin using Composer](#i-installing-dotkernel-admin-using-composer), the setup asks for configuration settings regarding injections (type `0` and hit `enter`) and the confirmation to use this setting for other packages (type `y` and hit `Enter`). ## Configuration - First Run - Remove the `.dist` extension from the files `config/autoload/local.php.dist` - Edit `config/autoload/local.php` according to your dev machine and fill in the `database` configuration +> Charset recommendation: utf8mb4_general_ci + Run the migrations and seeds with these commands: ```shell -php bin/doctrine-migrations migrate +php ./bin/doctrine-migrations migrate ``` ```shell -php bin/doctrine fixtures:execute +php ./bin/doctrine fixtures:execute ``` -- If you use `composer create-project`, the project will go into development mode automatically after installing. The - development mode status can be checked and toggled by using these composer commands: +- If you use `composer create-project`, the project will go into development mode automatically after installing. -You can check if you have development mode enabled by running: +The development mode status can be checked and toggled by using the below Composer commands: + +Check development status by running: ```shell composer development-status ``` -You can enable development mode by running: +Enable development mode by running: ```shell composer development-enable ``` -You can disable development mode by running: +Disable development mode by running: ```shell composer development-disable ``` - If not already done on installation, remove the `.dist` extension from `config/autoload/development.global.php.dist`. - This will enable dev mode by turning debug flag to `true` and turning configuration caching to `off`. It will also - make sure that any existing config cache is cleared. -> Charset recommendation: utf8mb4_general_ci +This will enable dev mode by turning debug flag to `true` and turning configuration caching to `off`. +It will also make sure that any existing config cache is cleared. ## Manage GeoLite2 database You can download/update a specific GeoLite2 database, by running the following command: ```shell -php bin/cli.php geoip:synchronize -d {DATABASE} +php ./bin/cli.php geoip:synchronize -d {DATABASE} ``` Where _{DATABASE}_ takes one of the following values: `asn`, `city`, `country`. @@ -162,26 +162,24 @@ Where _{DATABASE}_ takes one of the following values: `asn`, `city`, `country`. You can download/update all GeoLite2 databases at once, by running the following command: ```shell -php bin/cli.php geoip:synchronize +php ./bin/cli.php geoip:synchronize ``` -The output should be similar to the below, displaying per -row: `database identifier`: `previous build datetime` -> `current build datetime`. +The output should be similar to the below, displaying per row: -> asn: n/a -> 2021-07-01 02:09:34 -> -> city: n/a -> 2021-07-01 02:09:20 -> -> country: n/a -> 2021-07-01 02:05:12 +```shell +asn: n/a -> 2015-10-21 04:29:00 +city: n/a -> 2015-10-21 04:29:00 +country: n/a -> 2015-10-21 04:29:00 +``` Get help for this command by running: ```shell -php bin/cli.php help geoip:synchronize +php ./bin/cli.php help geoip:synchronize ``` -**Tip**: If you setup the synchronizer command as a cronjob, you can add the `-q|--quiet` option, and it will output -data only if an error has occurred. +> If you set up the synchronizer command as a cronjob, you can add the `-q|--quiet` option, and it will output data only if an error has occurred. ## NPM Commands @@ -191,8 +189,8 @@ To install dependencies into the `node_modules` directory run this command. npm install ``` -If `npm install` fails, this could be caused by user permissions of npm. Recommendation is to install npm -through `Node Version Manager`. +If `npm install` fails, this could be caused by user permissions of npm. +Recommendation is to install npm through `Node Version Manager`. The watch command compiles the components then watches the files and recompiles when one of them changes. @@ -208,15 +206,12 @@ npm run prod ## Authorization Guards -The packages responsible for restricting access to certain parts of the application -are [dot-rbac-guard](https://github.com/dotkernel/dot-rbac-guard) and [dot-rbac](https://github.com/dotkernel/dot-rbac). -These packages work together to create an infrastructure that is customizable and diversified to manage user access to -the platform by specifying the type of role the user has. +The packages responsible for restricting access to certain parts of the application are [dot-rbac-guard](https://github.com/dotkernel/dot-rbac-guard) and [dot-rbac](https://github.com/dotkernel/dot-rbac). +These packages work together to create an infrastructure that is customizable and diversified to manage user access to the platform by specifying the type of role the user has. -The `authorization.global.php` file provides multiple configurations specifying multiple roles as well as the types of -permissions to which these roles have access. +The `authorization.global.php` file provides multiple configurations specifying multiple roles as well as the types of permissions to which these roles have access. -```text +```php 'roles' => [ 'superuser' => [ 'permissions' => [ @@ -235,11 +230,9 @@ permissions to which these roles have access. ] ``` -The `authorization-guards.global.php` file provides configuration to restrict access to certain actions based on the -permissions defined in `authorization.global.php` so basically we have to add the permissions in the dot-rbac -configuration file first to specify the action restriction permissions. +The `authorization-guards.global.php` file provides configuration to restrict access to certain actions based on the permissions defined in `authorization.global.php` so basically we have to add the permissions in the dot-rbac configuration file first to specify the action restriction permissions. -```text +```php 'rules' => [ [ 'route' => 'account', @@ -264,9 +257,9 @@ configuration file first to specify the action restriction permissions. ] ``` -## Testing (Running) +## Testing (Running) the application -Note: **Do not enable dev mode in production** +> **Do not enable dev mode in production** - Run the following command in your project's directory to start PHPs built-in server: @@ -280,31 +273,27 @@ php -S 0.0.0.0:8080 -t public `127.0.0.1` means that the server can only be accessed locally (localhost only) `8080` the port on which the server is started (the listening port for the server) -**NOTE:** -If you are still getting exceptions or errors regarding some missing services, try running the following command +> If you are still getting exceptions or errors regarding some missing services, try running the following command: ```shell -php bin/clear-config-cache.php +php ./bin/clear-config-cache.php ``` -> If `config-cache.php` is present that config will be loaded regardless of the `ConfigAggregator::ENABLE_CACHE` -> in `config/autoload/mezzio.global.php` +> If `config-cache.php` is present that config will be loaded regardless of the `ConfigAggregator::ENABLE_CACHE` in `config/autoload/mezzio.global.php` - Open a web browser and visit `http://localhost:8080/` You should see the `Dotkernel admin` login page. - If you ran the migrations you will have an admin user in the database with the following credentials: - **User**: `admin` - **Password**: `dotadmin` -**NOTE:** - -- **Production only**: Make sure you modify the default admin credentials. -- **Development only**: `session.cookie_secure` does not work locally so make sure you modify your `local.php`, as per the following: +> **Production only**: Make sure you modify the default admin credentials. +> +> **Development only**: `session.cookie_secure` does not work locally so make sure you modify your `local.php`, as per the following: -```text +```php return [ 'session_config' => [ 'cookie_secure' => false, diff --git a/SECURITY.md b/SECURITY.md index 860d26e2..e72f271e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,14 +2,13 @@ ## Supported Versions - -| Version | Supported | PHP Version | -| ------- | ------------------ |-------------| -| 5.x | :white_check_mark: |![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/admin/5.0.x-dev)| -| 4.x | :white_check_mark: |![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/admin/4.0.1)| -| 3.x | :x: |![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/admin/3.0.x-dev)| -| <= 2.0 | :x: | | - +| Version | Supported | +|---------|-------------------------------| +| 6.x | :white_check_mark: | +| 5.x | :white_check_mark: | +| 4.x | :warning: (security-fix only) | +| 3.x | :x: | +| <= 2.0 | :x: | ## Reporting Potential Security Issues @@ -29,7 +28,6 @@ release prior to any public exposure; this helps protect the project's users, and provides them with a chance to upgrade and/or update in order to protect their applications. - ## Policy If we verify a reported security vulnerability, our policy is: @@ -39,4 +37,3 @@ If we verify a reported security vulnerability, our policy is: - After patching the release branches, we will immediately issue new security fix releases for each patched release branch. - diff --git a/composer.json b/composer.json index 91c238e2..7c95afb7 100644 --- a/composer.json +++ b/composer.json @@ -49,6 +49,7 @@ "laminas/laminas-i18n": "^2.26.0", "laminas/laminas-math": "^3.7.0", "mezzio/mezzio": "^3.18.0", + "mezzio/mezzio-authentication-oauth2": "^2.11", "mezzio/mezzio-authorization-rbac": "^1.7.0", "mezzio/mezzio-cors": "^1.11.1", "mezzio/mezzio-fastroute": "^3.11.0", @@ -70,13 +71,17 @@ }, "autoload": { "psr-4": { - "Admin\\App\\": "src/App/src/", "Admin\\Admin\\": "src/Admin/src/", - "Admin\\Page\\": "src/Page/src/", + "Admin\\App\\": "src/App/src/", "Admin\\Dashboard\\": "src/Dashboard/src/", + "Admin\\Page\\": "src/Page/src/", "Admin\\Setting\\": "src/Setting/src/", + "Admin\\User\\": "src/User/src/", + "Core\\Admin\\": "src/Core/src/Admin/src", "Core\\App\\": "src/Core/src/App/src", - "Core\\Admin\\": "src/Core/src/Admin/src" + "Core\\Security\\": "src/Core/src/Security/src", + "Core\\Setting\\": "src/Core/src/Setting/src", + "Core\\User\\": "src/Core/src/User/src" } }, "autoload-dev": { diff --git a/config/autoload/authorization-guards.global.php b/config/autoload/authorization-guards.global.php index 0261016e..b5b0177f 100644 --- a/config/autoload/authorization-guards.global.php +++ b/config/autoload/authorization-guards.global.php @@ -15,29 +15,36 @@ 'options' => [ 'guards' => [ [ - 'type' => 'ControllerPermission', + 'type' => 'RoutePermission', 'options' => [ 'rules' => [ - [ - 'route' => 'admin', - 'actions' => ['login'], - 'permissions' => ['*'], - ], - [ - 'route' => 'admin', - 'actions' => [], - 'permissions' => ['authenticated'], - ], - [ - 'route' => 'dashboard', - 'actions' => [], - 'permissions' => ['authenticated'], - ], - [ - 'route' => 'page', - 'actions' => [], - 'permissions' => ['authenticated'], - ], + 'admin::admin-login-form' => ['unauthenticated'], + 'admin::admin-login' => ['unauthenticated'], + 'admin::admin-create-form' => ['authenticated'], + 'admin::admin-create' => ['authenticated'], + 'admin::admin-delete-form' => ['authenticated'], + 'admin::admin-delete' => ['authenticated'], + 'admin::admin-edit-form' => ['authenticated'], + 'admin::admin-edit' => ['authenticated'], + 'admin::admin-list' => ['authenticated'], + 'admin::admin-login-list' => ['authenticated'], + 'admin::account-change-password' => ['authenticated'], + 'admin::account-edit-form' => ['authenticated'], + 'admin::account-edit' => ['authenticated'], + 'admin::admin-logout' => ['authenticated'], + 'app::index-redirect' => ['authenticated'], + 'dashboard::dashboard-view' => ['authenticated'], + 'page::components' => ['authenticated'], + 'setting::setting-store' => ['authenticated'], + 'setting::setting-view' => ['authenticated'], + 'user::user-create-form' => ['authenticated'], + 'user::user-create' => ['authenticated'], + 'user::user-edit-form' => ['authenticated'], + 'user::user-edit' => ['authenticated'], + 'user::user-delete-form' => ['authenticated'], + 'user::user-delete' => ['authenticated'], + 'user::user-list' => ['authenticated'], + 'user::user-avatar-edit' => ['authenticated'], ], ], ], diff --git a/config/autoload/cli.global.php b/config/autoload/cli.global.php index ecde79e6..d9ece9a0 100644 --- a/config/autoload/cli.global.php +++ b/config/autoload/cli.global.php @@ -2,8 +2,9 @@ declare(strict_types=1); -use Dot\Cli\Command\DemoCommand; +use Core\App\Command\RouteListCommand; use Dot\Cli\FileLockerInterface; +use Dot\GeoIP\Command\GeoIpCommand; return [ /** @@ -13,8 +14,8 @@ 'version' => '1.0.0', 'name' => 'Dotkernel CLI', 'commands' => [ - DemoCommand::getDefaultName() => DemoCommand::class, - Dot\GeoIP\Command\GeoIpCommand::getDefaultName() => Dot\GeoIP\Command\GeoIpCommand::class, + RouteListCommand::getDefaultName() => RouteListCommand::class, + GeoIpCommand::getDefaultName() => GeoIpCommand::class, ], ], FileLockerInterface::class => [ diff --git a/config/autoload/dependencies.global.php b/config/autoload/dependencies.global.php index a85e156f..f793c254 100644 --- a/config/autoload/dependencies.global.php +++ b/config/autoload/dependencies.global.php @@ -5,36 +5,25 @@ use Admin\App\Factory\AuthMiddlewareFactory; use Admin\App\Middleware\AuthMiddleware; use Dot\Authorization\AuthorizationInterface; -use Dot\ErrorHandler\ErrorHandlerInterface; -use Dot\ErrorHandler\LogErrorHandler; -use Dot\Mail\Factory\MailOptionsAbstractFactory; -use Dot\Mail\Factory\MailServiceAbstractFactory; -use Dot\Mail\Service\MailService; use Dot\Rbac\Authorization\AuthorizationService; return [ // Provides application-wide services. - // We recommend using fully-qualified class names whenever possible as - // service names. + // We recommend using fully-qualified class names whenever possible as service names. 'dependencies' => [ - // Use 'aliases' to alias a service name to another service. The - // key is the alias name, the value is the service to which it points. + // Use 'aliases' to alias a service name to another service. + // The key is the alias name, the value is the service to which it points. 'aliases' => [ - ErrorHandlerInterface::class => LogErrorHandler::class, AuthorizationInterface::class => AuthorizationService::class, - MailService::class => 'dot-mail.service.default', ], - // Use 'invokables' for constructor-less services, or services that do - // not require arguments to the constructor. Map a service name to the - // class name. + // Use 'invokables' for constructor-less services, or services that do not require arguments to the constructor. + // Map a service name to the class name. 'invokables' => [ // Fully\Qualified\InterfaceName::class => Fully\Qualified\ClassName::class, ], // Use 'factories' for services provided by callbacks/factory classes. 'factories' => [ - 'dot-mail.options.default' => MailOptionsAbstractFactory::class, - 'dot-mail.service.default' => MailServiceAbstractFactory::class, - AuthMiddleware::class => AuthMiddlewareFactory::class, + AuthMiddleware::class => AuthMiddlewareFactory::class, ], ], ]; diff --git a/config/autoload/local.php.dist b/config/autoload/local.php.dist index a974f83d..d08c545a 100644 --- a/config/autoload/local.php.dist +++ b/config/autoload/local.php.dist @@ -14,7 +14,7 @@ $baseUrl = 'http://localhost:8080'; $databases = [ 'default' => [ 'host' => 'localhost', - 'dbname' => '', + 'dbname' => 'dotkernel', 'user' => '', 'password' => '', 'port' => 3306, @@ -26,21 +26,28 @@ $databases = [ ]; return [ - 'application' => [ + 'application' => [ 'name' => $app['name'] ?? '', 'url' => $baseUrl, ], - 'routes' => [ + 'routes' => [ 'page' => [ 'component' => 'components', ], ], - 'databases' => $databases, - 'doctrine' => [ + 'databases' => $databases, + 'doctrine' => [ 'connection' => [ 'orm_default' => [ 'params' => $databases['default'], ], ], ], + 'uploads' => [ + 'user' => [ + 'url' => $baseUrl . '/uploads/user', + 'path' => realpath(__DIR__ . '/../../public/uploads/user'), + ], + ], + 'userAnonymizeAppend' => '', ]; diff --git a/config/autoload/navigation.global.php b/config/autoload/navigation.global.php index 542f94f1..ef29987e 100644 --- a/config/autoload/navigation.global.php +++ b/config/autoload/navigation.global.php @@ -17,19 +17,19 @@ 'route' => [ 'route_name' => 'dashboard::dashboard-view', ], - 'icon' => 'c-blue-500 ti-home', + 'icon' => 'c-blue-500 fa fa-home', ], ], [ 'options' => [ - 'label' => 'Manage admins', + 'label' => 'Admin', 'route' => [], - 'icon' => 'c-teal-500 ti-view-list-alt ', + 'icon' => 'c-teal-500 fa fa-user-secret', ], 'pages' => [ [ 'options' => [ - 'label' => 'Admins', + 'label' => 'Admin accounts', 'route' => [ 'route_name' => 'admin::admin-list', ], @@ -37,7 +37,7 @@ ], [ 'options' => [ - 'label' => 'Logins', + 'label' => 'Login attempts', 'route' => [ 'route_name' => 'admin::admin-login-list', ], @@ -45,13 +45,30 @@ ], ], ], + [ + 'options' => [ + 'label' => 'User', + 'route' => [], + 'icon' => 'c-teal-500 fa fa-user', + ], + 'pages' => [ + [ + 'options' => [ + 'label' => 'User accounts', + 'route' => [ + 'route_name' => 'user::user-list', + ], + ], + ], + ], + ], [ 'options' => [ 'label' => 'Components', 'route' => [ 'route_name' => 'page::components', ], - 'icon' => 'c-pink-500 ti-palette', + 'icon' => 'c-pink-500 fa fa-gears', ], ], ], diff --git a/config/config.php b/config/config.php index e2852e96..c44e1ff7 100644 --- a/config/config.php +++ b/config/config.php @@ -2,10 +2,6 @@ declare(strict_types=1); -use Laminas\ConfigAggregator\ArrayProvider; -use Laminas\ConfigAggregator\ConfigAggregator; -use Laminas\ConfigAggregator\PhpFileProvider; - // To enable or disable caching, set the `ConfigAggregator::ENABLE_CACHE` boolean in // `config/autoload/local.php`. $cacheConfig = [ @@ -13,7 +9,7 @@ ]; // @codingStandardsIgnoreStart -$aggregator = new ConfigAggregator([ +$aggregator = new Laminas\ConfigAggregator\ConfigAggregator([ // Laminas packages Laminas\Diactoros\ConfigProvider::class, Laminas\Form\ConfigProvider::class, @@ -26,6 +22,11 @@ Mezzio\Router\ConfigProvider::class, Mezzio\Router\FastRouteRouter\ConfigProvider::class, Mezzio\Twig\ConfigProvider::class, + class_exists(Mezzio\Tooling\ConfigProvider::class) + ? Mezzio\Tooling\ConfigProvider::class + : function () { + return []; + }, // Dotkernel packages Dot\Cache\ConfigProvider::class, @@ -46,27 +47,36 @@ Dot\Twig\ConfigProvider::class, // Include cache configuration - new ArrayProvider($cacheConfig), + new Laminas\ConfigAggregator\ArrayProvider($cacheConfig), // Dotkernel modules - Admin\App\ConfigProvider::class, Admin\Admin\ConfigProvider::class, - Admin\Setting\ConfigProvider::class, - Admin\Page\ConfigProvider::class, + Admin\App\ConfigProvider::class, Admin\Dashboard\ConfigProvider::class, - - Core\App\ConfigProvider::class, + Admin\Page\ConfigProvider::class, + Admin\Setting\ConfigProvider::class, + Admin\User\ConfigProvider::class, Core\Admin\ConfigProvider::class, + Core\App\ConfigProvider::class, + Core\Security\ConfigProvider::class, + Core\Setting\ConfigProvider::class, + Core\User\ConfigProvider::class, + // Load application config in a pre-defined order in such a way that local settings // overwrite global settings. (Loaded as first to last): // - `global.php` // - `*.global.php` // - `local.php` // - `*.local.php` - new PhpFileProvider(realpath(__DIR__) . '/autoload/{{,*.}global,{,*.}local,{,*.}test}.php'), + // - `local.test.php` + new Laminas\ConfigAggregator\PhpFileProvider( + realpath(__DIR__) . '/autoload/{{,*.}global,{,*.}local,{,*.}test}.php' + ), // Load development config if it exists - new PhpFileProvider(realpath(__DIR__) . '/development.config.php'), + new Laminas\ConfigAggregator\PhpFileProvider( + realpath(__DIR__) . '/development.config.php' + ), ], $cacheConfig['config_cache_path']); // @codingStandardsIgnoreEnd diff --git a/phpstan.neon b/phpstan.neon index 2ee1ec0a..8ae10027 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -8,21 +8,15 @@ parameters: - test treatPhpDocTypesAsCertain: false ignoreErrors: - - '/Parameter #1 \$inputFilter of method .*::setInputFilter\(\) expects Laminas\\InputFilter\\InputFilterInterface<[^>]+>, [^ ]+ given\./' - - '/Parameter #1 \$expected of method PHPUnit\\Framework\\Assert::assertSame\(\) contains unresolvable type\./' - - message: '#Call to method PHPUnit\\Framework\\Assert::assertInstanceOf\(\) with .* will always evaluate to true.#' - path: test + message: '#Parameter \#1 \$inputFilter of method Admin\\Admin\\Form\\CreateAdminForm::setInputFilter\(\) expects Laminas\\InputFilter\\InputFilterInterface, Admin\\Admin\\InputFilter\\EditAdminInputFilter given.#' + path: test/Unit/Admin/Form/AdminFormTest.php + - + message: '#Parameter \#1 \$expected of method PHPUnit\\Framework\\Assert::assertSame\(\) contains unresolvable type.#' + path: test/Unit/App/Plugin/FormsPluginTest.php - message: '#Call to an undefined method Laminas\\InputFilter\\InputFilterInterface::getInputs\(\)#' path: test - message: '#Call to an undefined method Laminas\\InputFilter\\InputFilterInterface::init\(\)#' path: src - - - message: '#Call to an undefined method Laminas\\Authentication\\AuthenticationServiceInterface::getAdapter\(\)#' - path: src/Admin/src/Handler/Account/PostAccountLoginHandler.php - - - message: '#Call to an undefined method Laminas\\Authentication\\AuthenticationServiceInterface::getStorage\(\)#' - path: src/Admin/src/Handler/Account/PostAccountLoginHandler.php - diff --git a/public/images/no-image.svg b/public/images/no-image.svg new file mode 100644 index 00000000..a6eb5e41 --- /dev/null +++ b/public/images/no-image.svg @@ -0,0 +1,15 @@ + + + + + + + + + + no-image + + + + + \ No newline at end of file diff --git a/public/js/admin.js b/public/js/admin.js index edf8271e..95d1a920 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -1 +1 @@ -$(document).ready((()=>{const t=async(t,o={})=>{try{const a=await fetch(t,o),d=await a.text();if(!a.ok)throw{data:d};return d}catch(t){throw{data:t.data}}};$("#add-admin-modal").on("show.bs.modal",(function(){const o=$(this),a=o.data("add-url");t(a,{method:"GET"}).then((t=>{o.find(".modal-dialog").html(t)})).catch((t=>{console.error("Error",t)}))})),$("#edit-admin-modal").on("show.bs.modal",(function(){const o=$(".ui-checkbox:checked");if(1!==o.length)return;const a=$(this),d=o.data("edit-url");t(d,{method:"GET"}).then((t=>{a.find(".modal-dialog").html(t)})).catch((()=>{location.reload()}))})),$("#delete-admin-modal").on("show.bs.modal",(function(){const o=$(".ui-checkbox:checked");if(1!==o.length)return;const a=$(this),d=o.data("delete-url");t(d,{method:"GET"}).then((t=>{a.find(".modal-dialog").html(t)})).catch((()=>{location.reload()}))})),$(document).on("submit","#admin-form",(o=>{o.preventDefault();const a=o.target;if(!a.checkValidity())return o.stopPropagation(),void a.classList.add("was-validated");const d=a.getAttribute("action"),e=$(a.closest(".modal"));t(d,{method:"POST",body:new FormData(a)}).then((t=>{location.reload()})).catch((t=>{console.error("Error",t),e.find(".modal-dialog").html(t.data)}))})),$(document).on("submit","#delete-admin-form",(o=>{o.preventDefault();const a=o.target;if(!a.checkValidity())return o.stopPropagation(),void a.classList.add("was-validated");const d=a.getAttribute("action"),e=$(a.closest(".modal"));t(d,{method:"POST",body:new FormData(a)}).then((()=>{location.reload()})).catch((t=>{e.find(".modal-dialog").html(t.data)}))}))})); \ No newline at end of file +$(document).ready((()=>{const t=async(t,o={})=>{try{const d=await fetch(t,o),a=await d.text();if(!d.ok)throw{data:a};return a}catch(t){throw{data:t.data}}};$("#add-admin-modal").on("show.bs.modal",(function(){const o=$(this);t(o.data("add-url"),{method:"GET"}).then((t=>{o.find(".modal-dialog").html(t)})).catch((t=>{console.error("Error",t)}))})).on("hidden.bs.modal",(function(){$(this).find(".modal-dialog").find(".modal-body").html("Loading...")})),$("#edit-admin-modal").on("show.bs.modal",(function(){const o=$(".ui-checkbox:checked");if(1!==o.length)return;const d=$(this);t(o.data("edit-url"),{method:"GET"}).then((t=>{d.find(".modal-dialog").html(t)})).catch((()=>{location.reload()}))})).on("hidden.bs.modal",(function(){$(this).find(".modal-dialog").find(".modal-body").html("Loading...")})),$("#delete-admin-modal").on("show.bs.modal",(function(){const o=$(".ui-checkbox:checked");if(1!==o.length)return;const d=$(this);t(o.data("delete-url"),{method:"GET"}).then((t=>{d.find(".modal-dialog").html(t)})).catch((()=>{location.reload()}))})).on("hidden.bs.modal",(function(){$(this).find(".modal-dialog").find(".modal-body").html("Loading...")})),$(document).on("submit","#admin-form",(o=>{o.preventDefault();const d=o.target;if(!d.checkValidity())return o.stopPropagation(),void d.classList.add("was-validated");const a=$(d.closest(".modal"));t(d.getAttribute("action"),{method:"POST",body:new FormData(d)}).then((t=>{location.reload()})).catch((t=>{console.error("Error",t),a.find(".modal-dialog").html(t.data)}))})),$(document).on("submit","#delete-admin-form",(o=>{o.preventDefault();const d=o.target;if(!d.checkValidity())return o.stopPropagation(),void d.classList.add("was-validated");const a=$(d.closest(".modal"));t(d.getAttribute("action"),{method:"POST",body:new FormData(d)}).then((()=>{location.reload()})).catch((t=>{a.find(".modal-dialog").html(t.data)}))}))})); \ No newline at end of file diff --git a/public/js/table_settings.js b/public/js/table_settings.js index 5b1c0ec9..303cb3c7 100644 --- a/public/js/table_settings.js +++ b/public/js/table_settings.js @@ -1 +1 @@ -$((function(){if(!storeSettingsUrl)return void console.error("Invalid or no storeSettingsUrl provided.");if(!getSettingsUrl)return void console.error("Invalid or no getSettingsUrl provided.");if(!tableId)return void console.error("Invalid or no tableId provided.");const e=(e,t)=>{const c=$(e);0!==t.length?($(".table-column").each(((e,c)=>{const n=$(c).data("column");o(n,t.includes(n))})),c.show()):c.show()},o=(e,o)=>{o?$(`.column-${e}`).show():$(`.column-${e}`).hide()},t=(e,o)=>{const t=$(e);$(".table-column").map(((e,o)=>({text:$(o).text().trim(),column:$(o).data("column")}))).each(((e,c)=>{t.append($("
  • ").append($("
    ").addClass("dropdown-item pT-0 pB-0").append($("").prop("type","checkbox").prop("id",`column-selector-${c.column}`).prop("checked",o.includes(c.column)||0===o.length).addClass("toggle-column-checkbox").data("column",c.column)).append($("
  • ").append($("
    ").addClass("dropdown-item pT-0 pB-0").append($("").prop("type","checkbox").prop("id",`column-selector-${c.column}`).prop("checked",o.includes(c.column)||0===o.length).addClass("toggle-column-checkbox").data("column",c.column)).append($("