From 06dd7fd4cfb09178ca4e30a658c36406a3f4fc0f Mon Sep 17 00:00:00 2001 From: bidi Date: Wed, 23 Sep 2026 18:04:43 +0300 Subject: [PATCH] updated docs based on dotkernel/frontend Signed-off-by: bidi --- docs/book/v5/how-to/authorization.md | 109 ++++++++++++------ docs/book/v5/how-to/creating-fixtures.md | 21 ++++ docs/book/v5/how-to/creating-migrations.md | 34 +++++- docs/book/v5/how-to/csrf.md | 38 +++++- docs/book/v5/how-to/dependency-injection.md | 39 ++++++- docs/book/v5/how-to/npm_commands.md | 32 ++++- docs/book/v5/installation/composer.md | 39 +++++-- .../v5/installation/configuration-files.md | 54 +++++++-- docs/book/v5/installation/development-mode.md | 36 +++++- docs/book/v5/installation/doctrine-orm.md | 32 ++++- docs/book/v5/installation/faq.md | 20 ++++ docs/book/v5/installation/getting-started.md | 21 +++- .../v5/installation/installation-intro.md | 24 ++++ .../v5/installation/running-application.md | 29 +++++ docs/book/v5/introduction/file-structure.md | 53 +++++++-- docs/book/v5/introduction/introduction.md | 31 ++++- docs/book/v5/introduction/packages.md | 27 +++++ .../v5/introduction/server-requirements.md | 36 +++++- .../v5/reference/account-anonymization.md | 39 ++++++- 19 files changed, 624 insertions(+), 90 deletions(-) diff --git a/docs/book/v5/how-to/authorization.md b/docs/book/v5/how-to/authorization.md index b7b1f2c..b027cb6 100644 --- a/docs/book/v5/how-to/authorization.md +++ b/docs/book/v5/how-to/authorization.md @@ -1,54 +1,93 @@ # Authorization Guards +## Summary + +This page explains how Dotkernel Frontend restricts access: roles and their permissions are defined in `authorization.global.php`, and `authorization-guards.global.php` requires those permissions for specific routes and controller actions. + +## Details + 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. ```php -//example of a flat RBAC model that specifies two types of roles as well as their permission - 'roles' => [ - 'admin' => [ +// config/autoload/authorization.global.php - flat RBAC model shipped with Frontend +'dot_authorization' => [ + 'guest_role' => 'guest', + 'role_provider' => [ + 'type' => 'InMemory', + 'options' => [ + 'roles' => [ + 'user' => [ 'permissions' => [ 'authenticated', - 'edit', - 'delete', - //etc.. - ] + 'premium', + ], ], - 'user' => [ + 'guest' => [ 'permissions' => [ - 'authenticated', - //etc.. - ] - ] - ] + 'unauthenticated', + ], + ], + ], + ], + ], +], ``` 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. ```php -// configuration example to restrict certain actions of some routes based on the permissions specified in the dot-rbac configuration file +// config/autoload/authorization-guards.global.php - ControllerPermission guard shipped with Frontend +'type' => 'ControllerPermission', +'options' => [ 'rules' => [ - [ - 'route' => 'account', - 'actions' => [//list of actions to apply , or empty array for all actions - 'unregister', - 'avatar', - 'details', - 'changePassword' - ], - 'permissions' => ['authenticated'] - ], - [ - 'route' => 'admin', - 'actions' => [ - 'deleteAccount' - ], - 'permissions' => [ - 'delete' - //list of roles to allow - ] - ] - ] + [ + 'route' => 'account', + 'actions' => [ // list of actions to apply, or empty array for all actions + 'avatar', + 'details', + 'changePassword', + 'deleteAccount', + ], + 'permissions' => ['authenticated'], + ], + [ + 'route' => 'page', + 'actions' => [ + 'premium-content', + ], + 'permissions' => ['premium'], // list of permissions required + ], + ], +], ``` + +> The default `protection_policy` is `GuardInterface::POLICY_ALLOW`: routes and actions without a rule are accessible to everyone. + +## FAQ + +### **Q: What happens to routes and actions that have no rule?** + +A: They are open to everyone. +The shipped `protection_policy` is `GuardInterface::POLICY_ALLOW`. + +### **Q: Which role does a visitor who is not logged in have?** + +A: `guest`, as set by `guest_role` in `authorization.global.php`. +It has only the `unauthenticated` permission. + +### **Q: How do I protect every action of a route?** + +A: Add a rule for the route with an empty `actions` array. + +### **Q: What is the `premium` permission for?** + +A: It protects the `premium-content` action of the `page` route (`/page/premium-content`). +The shipped `user` role has it, so any logged-in user can open the page. + +### **Q: Where are a user's roles stored?** + +A: In the `user_role` table, linked to users through `user_roles`. +The default roles are created by the `RoleLoader` fixture. diff --git a/docs/book/v5/how-to/creating-fixtures.md b/docs/book/v5/how-to/creating-fixtures.md index a0610bc..e6a4515 100644 --- a/docs/book/v5/how-to/creating-fixtures.md +++ b/docs/book/v5/how-to/creating-fixtures.md @@ -1,5 +1,11 @@ # Fixtures +## Summary + +This page lists and runs the Doctrine data fixtures that seed the database, either all at once or one class at a time. + +## Details + > Fixtures are used to seed the database with initial values and should only be executed ONCE each, after migrating the database. Seeding the database is done with the help of our custom package `dotkernel/dot-data-fixtures` built on top of `doctrine/data-fixtures`. @@ -30,3 +36,18 @@ php bin/doctrine fixtures:execute --class=RoleLoader Fixtures can and should be ordered to ensure database consistency. More on ordering fixtures can be found here : https://www.doctrine-project.org/projects/doctrine-data-fixtures/en/latest/how-to/fixture-ordering.html#fixture-ordering + +## FAQ + +### **Q: Where do fixture classes go?** + +A: In `data/doctrine/fixtures`, the path set by `doctrine` => `fixtures` in `config/autoload/doctrine.global.php`. + +### **Q: What value does `--class` take?** + +A: The class's short name, which must match its file name without `.php`, for example `--class=RoleLoader` for `RoleLoader.php`. + +### **Q: Why should each fixture run only once?** + +A: `fixtures:execute` appends to the existing data instead of purging it. +Running a fixture twice inserts its rows again, which fails on unique columns such as `user_role.name`. diff --git a/docs/book/v5/how-to/creating-migrations.md b/docs/book/v5/how-to/creating-migrations.md index 9ee06d4..e943c0d 100644 --- a/docs/book/v5/how-to/creating-migrations.md +++ b/docs/book/v5/how-to/creating-migrations.md @@ -1,5 +1,11 @@ # Creating migrations +## Summary + +This page generates a new Doctrine migration file and shows how to write its `up` and `down` methods. + +## Details + Migrations are used to create and/or edit the database structure. To generate a new migration file, use this command: @@ -18,12 +24,32 @@ You can add new queries in: This example creates a new column named `test`. Add this in `public function up`: -```shell -$this->addSql('ALTER TABLE users ADD test VARCHAR(255) NOT NULL'); +```php +$this->addSql('ALTER TABLE user ADD test VARCHAR(255) NOT NULL'); ``` And its opposite in `public function down`: -```shell -$this->addSql('ALTER TABLE users DROP test'); +```php +$this->addSql('ALTER TABLE user DROP test'); ``` + +## FAQ + +### **Q: Where are new migrations saved?** + +A: In `data/doctrine/migrations`, in the `Frontend\Migrations` namespace, as set in `config/migrations.php`. + +### **Q: How do I undo the last migration?** + +A: Run `php vendor/bin/doctrine-migrations migrate prev`, which runs the `down` method of the latest executed migration. + +### **Q: Can Doctrine write the migration for me?** + +A: Yes. After changing your entities, run `php vendor/bin/doctrine-migrations diff`, which generates a migration from the difference between the entity mapping and the database. +Review the generated SQL before you run it. + +### **Q: Is a failed migration rolled back?** + +A: `config/migrations.php` enables `transactional` and `all_or_nothing`. +MySQL and MariaDB commit schema changes implicitly, so a failed `ALTER TABLE` may still leave earlier statements applied. diff --git a/docs/book/v5/how-to/csrf.md b/docs/book/v5/how-to/csrf.md index 967e7ca..e9dd437 100644 --- a/docs/book/v5/how-to/csrf.md +++ b/docs/book/v5/how-to/csrf.md @@ -1,5 +1,12 @@ # CSRF protection in forms +## Summary + +This page adds CSRF protection to a form in three steps: a `Csrf` form element, a CSRF validator in the input filter, and the rendered hidden field in the template. +It also shows how to test the protection and how the token timeout works. + +## Details + A Cross-Site Request Forgery (CSRF) attack is a type of security vulnerability that tricks a user into performing actions on a web application in which they are authenticated, without their knowledge or consent. Web applications can protect users against these types of attacks by implementing CSRF tokens in their forms which are known only to the application that generated them and must be included when submitting forms. @@ -33,7 +40,8 @@ $this->add(new \Laminas\Form\Element\Csrf('exampleCsrf', [ ### Validate field Open the InputFilter that validates the form fields and append the following code to the method that initializes the -fields (usually `init`): +fields (usually `init`). +If the form builds its input filter itself (for example `ProfileDetailsForm`), add it there instead: ```php $csrf = new \Laminas\InputFilter\Input('exampleCsrf'); @@ -76,6 +84,12 @@ After filling out the form, submitting it should work as before. In order to make sure that the new CSRF field works as expected, you can inspect the form using your browser's `Developer tools` and modify its value. Submitting a filled out form should result in a validation error: +```text +CSRF is invalid +``` + +Clearing the value instead results in: + ```text CSRF is required and cannot be empty ``` @@ -87,7 +101,27 @@ This represents the value in seconds for how long the token is valid. Submitting a form that has been rendered for longer than this value will result in a validation error: ```text -**CSRF** is invalid +CSRF is invalid ``` > You can modify the value of `timeout` in each form, but the default value should work in most cases. + +## FAQ + +### **Q: Which forms are already protected?** + +A: Every shipped form: login, register, request password reset, reset password, profile details, avatar upload, change password, delete account and contact. +Their fields are named after the form, for example `userLoginCsrf` and `contactCsrf`. + +### **Q: How long is a token valid?** + +A: 3600 seconds (one hour), set by the `timeout` option of each form's `Csrf` element. + +### **Q: Can two forms use the same CSRF field name?** + +A: Give each form its own name. +Each name keeps its own token in the session, so forms that share a name also share, and overwrite, the same token. + +### **Q: Which error appears when the token is wrong?** + +A: A missing token gives "CSRF is required and cannot be empty"; a modified or expired token gives "CSRF is invalid". diff --git a/docs/book/v5/how-to/dependency-injection.md b/docs/book/v5/how-to/dependency-injection.md index cc12f7d..86fc966 100644 --- a/docs/book/v5/how-to/dependency-injection.md +++ b/docs/book/v5/how-to/dependency-injection.md @@ -1,5 +1,11 @@ # Dependency Injection +## Summary + +This page explains how Dotkernel Frontend injects constructor dependencies with the `#[Inject]` attribute from `dot-dependency-injection`, and how to register a class with `AttributedServiceFactory` so that no factory has to be written. + +## Details + Dependency injection is a design pattern used in software development to implement inversion of control. In simpler terms, it's the act of providing dependencies for an object during instantiation. @@ -14,20 +20,21 @@ Dotkernel Frontend, through its [dot-dependency-injection](https://github.com/do `dot-dependency-injection` determines the dependencies by looking at the `#[Inject]` attribute, added to the constructor of a class. Each dependency is specified as a separate parameter of the `#[Inject]` attribute. -For our example we will inject `UserService` and `config` dependencies into a `UserHandler`. +For our example we will inject `UserServiceInterface` and `config` dependencies into a `UserController`. ```php +use Dot\Controller\AbstractActionController; use Dot\DependencyInjection\Attribute\Inject; -class UserHandler implements RequestHandlerInterface +class UserController extends AbstractActionController { #[Inject( - UserService::class, + UserServiceInterface::class, "config", )] public function __construct( protected UserServiceInterface $userService, - protected array $config, + protected array $config = [], ) { } } @@ -43,7 +50,7 @@ public function getDependencies(): array { return [ 'factories' => [ - UserHandler::class => AttributedServiceFactory::class + UserController::class => AttributedServiceFactory::class ] ]; } @@ -53,4 +60,24 @@ That's it. When your object is instantiated from the container, it will automatically have its dependencies resolved. > Dependencies injection is available to any object within Dotkernel Frontend. -> For example, you can inject dependencies in a service, a handler and so on, simply by registering them in the `ConfigProvider`. +> For example, you can inject dependencies in a service, a controller and so on, simply by registering them in the `ConfigProvider`. + +## FAQ + +### **Q: Do I still need to write factories?** + +A: Not for classes that use `#[Inject]`. +Register services and controllers with `AttributedServiceFactory`, and Doctrine repositories with `AttributedRepositoryFactory`, as `src/User/src/ConfigProvider.php` does. + +### **Q: How do I inject a single configuration key?** + +A: Use dot notation in the attribute. +For example, `RecaptchaService` uses `#[Inject("config.recaptcha")]` to receive only the `recaptcha` array. + +### **Q: Does the order of the `#[Inject]` arguments matter?** + +A: Yes. The dependencies are passed to the constructor in the order they are listed, so the list must match the order of the constructor parameters. + +### **Q: Does the package support setter or property injection?** + +A: No. `dot-dependency-injection` supports constructor injection only. diff --git a/docs/book/v5/how-to/npm_commands.md b/docs/book/v5/how-to/npm_commands.md index f03d9cf..1faaed3 100644 --- a/docs/book/v5/how-to/npm_commands.md +++ b/docs/book/v5/how-to/npm_commands.md @@ -1,5 +1,13 @@ # NPM Commands +## Summary + +This page installs the frontend build dependencies with npm and compiles the assets, either continuously while you develop or once, minified, for production. + +## Details + +> The Frontend interface has been tested with npm v10.0.4 and Node.js v20.11.0. + To install dependencies into the `node_modules` directory run this command. ```shell @@ -13,10 +21,32 @@ The watch command compiles the components then monitors the files for changes an ```shell npm run watch -``` +``` After all updates are done, this command compiles the assets locally, minifies them and makes them ready for production. ```shell npm run prod ``` + +To compile the assets once in development mode, without watching for changes, run: + +```shell +npm run dev +``` + +## FAQ + +### **Q: Where are the source assets, and where does the build write them?** + +A: The sources are in `src/App/assets`. +Webpack writes the compiled files to `public/css`, `public/js`, `public/fonts` and `public/images`. + +### **Q: Which Node.js and npm versions are supported?** + +A: The interface has been tested with npm v10.0.4 and Node.js v20.11.0. + +### **Q: Should I commit the compiled assets?** + +A: The repository tracks the compiled `public/css/app.css` and `public/js/app.js`. +Run `npm run prod` before you commit asset changes so that the committed files are minified. diff --git a/docs/book/v5/installation/composer.md b/docs/book/v5/installation/composer.md index 8a0f131..4088cdf 100644 --- a/docs/book/v5/installation/composer.md +++ b/docs/book/v5/installation/composer.md @@ -1,11 +1,20 @@ # Composer Installation of Packages +## Summary + +This page installs the PHP dependencies with `composer install` and explains how to answer the configuration prompts it shows. + +## Details + Composer is required to install Dotkernel Frontend. You can install Composer from the [official site](https://getcomposer.org/). > First make sure that you have navigated your command prompt to the folder where you copied the files in the previous step. ## Install dependencies +> The installation requires the PHP extension `intl`, which may not be enabled by default. +> If Composer reports `laminas/laminas-i18n ... requires ext-intl * -> the requested PHP extension intl is missing from your system.`, enable `extension=intl` in your `php.ini`. + Run this command in the command prompt. > Use the **CLI** in order to ensure interactivity for proper configuration. @@ -50,22 +59,28 @@ The next question is: Type `y` here, and hit `enter` to complete this stage. +After the packages are installed, `bin/composer-post-install-script.php` creates `config/autoload/local.php` (from `local.php.dist`) and `config/autoload/mail.global.php` (from dot-mail's `mail.global.php.dist`) if they are missing. + ## Development mode -If you're installing the project for development, make sure you have development mode enabled, by running: +Development mode is covered in [Development Mode](development-mode.md). -```shell -composer development-enable -``` +## FAQ -You can disable development mode by running: +### **Q: Why should I choose `[0] Do not inject` at the prompt?** -```shell -composer development-disable -``` +A: `config/config.php` already registers the ConfigProviders that the installer offers to inject. +Choosing `[1]` adds them a second time. -You can check if you have development mode enabled by running: +### **Q: The install fails because `ext-intl` is missing. What should I do?** -```shell -composer development-status -``` +A: Enable `extension=intl` in your `php.ini` and run `composer install` again; `laminas/laminas-i18n` requires it. + +### **Q: Which files does `composer install` create?** + +A: `bin/composer-post-install-script.php` creates `config/autoload/local.php` and `config/autoload/mail.global.php` if they are missing. +It creates `config/autoload/local.test.php` only when development mode is already enabled. + +### **Q: Where are the packages installed?** + +A: In the `vendor` folder. diff --git a/docs/book/v5/installation/configuration-files.md b/docs/book/v5/installation/configuration-files.md index 72ea339..ccb1c9a 100644 --- a/docs/book/v5/installation/configuration-files.md +++ b/docs/book/v5/installation/configuration-files.md @@ -1,40 +1,47 @@ # Configuration Files +## Summary + +This page prepares the local configuration files and fills in the database, mail, contact recipients and reCAPTCHA settings that Dotkernel Frontend needs. + +## Details + This step involves changing the names of or duplicating some of the `.dist` files in the project and editing their content to suit your project's requirements. ## Prepare config files -- Duplicate `config/autoload/debugbar.local.php.dist` as `config/autoload/debugbar.local.php` -- Duplicate `config/autoload/development.local.php.dist` as `config/autoload/development.local.php` -- Duplicate `config/autoload/local.php.dist` as `config/autoload/local.php` -- Duplicate `config/autoload/mail.local.php.dist` as `config/autoload/mail.local.php` -- Edit `config/autoload/local.php` according to your dev machine and fill in the `database` configuration. +- `config/autoload/development.local.php` is created from its `.dist` file when you run `composer development-enable`; do not duplicate it manually. +- `composer install` creates `config/autoload/mail.global.php` from `vendor/dotkernel/dot-mail/config/mail.global.php.dist`; duplicate it as `config/autoload/mail.local.php` for your credentials, because `*.local.php` files are ignored by git and override `*.global.php`. +- Edit `config/autoload/local.php` (created from `local.php.dist` by `composer install`) according to your dev machine and fill in the `database` configuration. > If you intend to send emails from your Frontend, make sure to fill in SMTP connection params. -> This will be covered in the next section. +> This is covered in the [Mail](#mail) section below. > **Optional**: in order to run/create tests, duplicate `config/autoload/local.test.php.dist` as `config/autoload/local.test.php`. > This creates a new in-memory database that your tests will run on. ## Mail -If you want your application to send mails on registration, contact etc. add valid credentials to the following keys in `config/autoload/mail.local.php` +If you want your application to send mails on registration, contact etc. add valid credentials to the following keys under `dot_mail` => `default` in `config/autoload/mail.local.php` Under `message_options` key: - `from` - email address that will send emails (required) - `from_name` - organization name for signing sent emails (optional) +To send through SMTP, set `transport` to `esmtp` (the default is `sendmail`). + Under `smtp_options` key: - `host` - hostname or IP address of the mail server (required) - `connection_config` - add the `username` and `password` keys (required) -In `config/autoload/local.php` edit the key `contact` => `message_receivers` => `to` with *string* values for emails that should receive contact messages. +In `config/autoload/local.php` edit the key `contact` => `message_recipients` => `to` with an array of email addresses that should receive contact messages. > **Please add at least 1 email address in order for contact message to reach someone** -Also feel free to add as many CCs as you require under the `contact` => `message_receivers` => `cc` key. +Also feel free to add as many CCs and BCCs as you require under the `contact` => `message_recipients` => `cc` and `bcc` keys. +The optional `contact` => `message_sender` => `from_email` and `from_name` keys override the sender set in the `dot_mail` configuration. ## reCAPTCHA @@ -45,6 +52,35 @@ Dotkernel frontend uses the Google reCAPTCHA for its `contact us` form. - Update the `recaptcha` array in `config/autoload/local.php` with the `siteKey` and `secretKey` from Google reCAPTCHA. +> The contact page throws an `Invalid siteKey provided.` exception until both keys are filled in. + +- Adjust `scoreThreshold` if needed (a float between `0.0` and `1.0`, default `0.5`); a submission passes only when Google's response `score` is higher than this value. + > You need to whitelist `localhost` in the reCAPTCHA settings page during development. >**When in production do not forget to either remove `localhost` from the reCAPTCHA whitelist, or have a separate reCAPTCHA** + +## FAQ + +### **Q: Which configuration files are safe for credentials?** + +A: `config/autoload/local.php` and any `config/autoload/*.local.php` file, which git ignores. +`mail.global.php` is not ignored, so put SMTP credentials in a `mail.local.php`. + +### **Q: Do I have to configure mail?** + +A: Only if the application should send the registration, activation, password reset and contact emails. +The default transport is `sendmail`; set `transport` to `esmtp` to send through an SMTP server. + +### **Q: Who receives the messages sent from the contact form?** + +A: The addresses under `contact` => `message_recipients` in `config/autoload/local.php`: `to`, plus optional `cc` and `bcc`. + +### **Q: Why does the contact page fail with "Invalid `siteKey` provided."?** + +A: The reCAPTCHA `siteKey` or `secretKey` in `config/autoload/local.php` is empty. +The contact page needs both keys before it can render. + +### **Q: What is `local.test.php` for?** + +A: It points Doctrine at an in-memory SQLite database, but only while the tests have enabled test mode, so your development database is never touched. diff --git a/docs/book/v5/installation/development-mode.md b/docs/book/v5/installation/development-mode.md index c736769..d4a8914 100644 --- a/docs/book/v5/installation/development-mode.md +++ b/docs/book/v5/installation/development-mode.md @@ -1,5 +1,11 @@ # Development mode +## Summary + +This page enables development mode, which turns on debugging, turns off configuration caching and clears any existing configuration cache. + +## Details + Run this command to 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. @@ -14,4 +20,32 @@ You should see this in the command prompt: You are now in development mode. ``` -- If not already done, remove the `.dist` extension from `config/autoload/development.local.php.dist`. +The command also creates `config/development.config.php` and `config/autoload/development.local.php` from their `.dist` files, so no manual copy is needed. + +You can disable development mode by running: + +```shell +composer development-disable +``` + +You can check if you have development mode enabled by running: + +```shell +composer development-status +``` + +## FAQ + +### **Q: What does development mode change?** + +A: `config/development.config.php` sets `debug` to `true` and disables the configuration cache. +`config/autoload/development.local.php` registers Whoops as the error response generator, which shows the exception details and stack trace. + +### **Q: How do I turn development mode off?** + +A: Run `composer development-disable`; `composer development-status` shows the current state. + +### **Q: Should development mode be enabled in production?** + +A: No. +It exposes exception details through Whoops and disables the configuration cache. diff --git a/docs/book/v5/installation/doctrine-orm.md b/docs/book/v5/installation/doctrine-orm.md index f8ba8ff..8f71b6d 100644 --- a/docs/book/v5/installation/doctrine-orm.md +++ b/docs/book/v5/installation/doctrine-orm.md @@ -1,5 +1,11 @@ # Doctrine ORM +## Summary + +This page connects Dotkernel Frontend to a MariaDB or MySQL database, creates the tables by running the migrations, and seeds the default user roles with the fixtures. + +## Details + This step saves the database connection credentials in a Frontend configuration file. We do not cover the creation steps of the database itself. @@ -57,14 +63,14 @@ Each migration will be logged in the `migrations` table to prevent running the s If everything ran correctly, you will get this confirmation. ```shell -[OK] Successfully migrated to version: Frontend\Migrations\Version20240806123413 +[OK] Successfully migrated to version: Frontend\Migrations\Version20241120160406 ``` -> The migration name `Version20240806123413` may differ in future Frontend updates. +> The migration name `Version20241120160406` may differ in future Frontend updates. ## Fixtures -Run this command to populate the `user_role` table with the default values: +Run this command to populate the `user_role` table with the default roles (`admin`, `user` and `guest`): ```shell php bin/doctrine fixtures:execute @@ -82,3 +88,23 @@ Fixtures have been loaded. ' <' `\ ._/'\ ` \ \ ``` + +## FAQ + +### **Q: Which character set and collation should the database use?** + +A: `utf8mb4` with `utf8mb4_general_ci`, the values set in `config/autoload/local.php.dist`. + +### **Q: Where is the list of executed migrations kept?** + +A: In the `migrations` table, as configured in `config/migrations.php`. +Doctrine Migrations uses it to skip migrations that have already run. + +### **Q: Which roles do the fixtures create?** + +A: `admin`, `user` and `guest`, in the `user_role` table. + +### **Q: Can I run the fixtures again?** + +A: Not on the same database. +The command appends data instead of purging it, and `user_role.name` is unique, so a second run fails on the duplicate role names. diff --git a/docs/book/v5/installation/faq.md b/docs/book/v5/installation/faq.md index 6e33591..f61cd53 100644 --- a/docs/book/v5/installation/faq.md +++ b/docs/book/v5/installation/faq.md @@ -1,5 +1,9 @@ # Frequently Asked Questions +## Summary + +This page solves the most common first-run errors, which come from the web server not being able to write to the `data`, `public/uploads` and `log` folders. + ## How do I fix common permission issues? If running your project you encounter some permission issues, follow the below steps. @@ -37,3 +41,19 @@ chmod -R 777 public/uploads ```shell chmod -R 777 log ``` + +## FAQ + +### **Q: Is `chmod -R 777` safe on a production server?** + +A: No. It lets every user on the server write to these folders. +In production, give ownership of `data`, `public/uploads` and `log` to the web server user and grant write access only to that user. + +### **Q: Where are the error logs?** + +A: In `log/error-log-{Y}-{m}-{d}.log`, one JSON-formatted file per day, as set in `config/autoload/error-handling.global.php`. + +### **Q: The application reports missing services after a configuration change. What should I do?** + +A: Remove the cached configuration with `php bin/clear-config-cache.php` (or `composer clear-config-cache`). +While `data/cache/config-cache.php` exists, it is loaded instead of your configuration files. diff --git a/docs/book/v5/installation/getting-started.md b/docs/book/v5/installation/getting-started.md index adc9522..8ec3e56 100644 --- a/docs/book/v5/installation/getting-started.md +++ b/docs/book/v5/installation/getting-started.md @@ -1,9 +1,14 @@ # Clone the project +## Summary + +This page downloads the Dotkernel Frontend source code into an empty directory with `git clone` and shows the output to expect. +It also recommends WSL2 as the development environment on Windows. + ## Recommended development environment > If you are using Windows as OS on your machine, you can use WSL2 as development environment. -> Read more here: [PHP-Mariadb-on-WLS2](https://www.dotkernel.com/php-development/almalinux-9-in-wsl2-install-php-apache-mariadb-composer-phpmyadmin/) +> Read more here: [Dotkernel development environment on WSL2](https://github.com/dotkernel/development/blob/main/wsl/README.md) Using your terminal, navigate inside the directory you want to download the project files into. @@ -32,3 +37,17 @@ Resolving deltas: 100% (3868/3868), done. ``` You can already open the project in your preferred IDE to double-check the files were copied correctly. + +## FAQ + +### **Q: Why does the directory have to be empty?** + +A: Cloning into `.` fails with `fatal: destination path '.' already exists and is not an empty directory.` when the directory contains any files, and nothing is downloaded. + +### **Q: Which branch does `git clone` install?** + +A: The default branch, `5.0`, including changes that have not been released yet. + +### **Q: I use Windows. What environment should I use?** + +A: WSL2, with one of the AlmaLinux distributions from `dotkernel/development`, as described in [Running the Application](running-application.md). diff --git a/docs/book/v5/installation/installation-intro.md b/docs/book/v5/installation/installation-intro.md index ee0dffe..208981c 100644 --- a/docs/book/v5/installation/installation-intro.md +++ b/docs/book/v5/installation/installation-intro.md @@ -1,5 +1,12 @@ # Introduction +## Summary + +This tutorial installs Dotkernel Frontend from scratch: cloning the project, installing dependencies, configuring it, preparing the database and running it. +Each step lists the commands to run and the output to expect. + +## Details + In this tutorial, we will install Dotkernel Frontend from scratch. We will focus on these tasks: @@ -9,3 +16,20 @@ We will focus on these tasks: - Run the project. By the end of this tutorial you will have a fully-functional Dotkernel Frontend on your selected environment and can begin coding. + +## FAQ + +### **Q: In which order should I follow the installation pages?** + +A: Getting Started, Composer, Configuration Files, Doctrine ORM, Development Mode, then Running the Application. +If you hit permission errors, see the FAQ page. + +### **Q: What do I need installed before I start?** + +A: Git, PHP 8.2 or 8.3 with the required extensions, Composer, and MariaDB or MySQL. +Node.js and npm are needed only if you change the frontend assets. + +### **Q: Can I install Frontend with `composer create-project`?** + +A: Yes. `composer create-project dotkernel/frontend` installs the latest release, and `composer.json` enables development mode after the project is created. +This tutorial uses `git clone` instead, which installs the current default branch even if it has not been released. diff --git a/docs/book/v5/installation/running-application.md b/docs/book/v5/installation/running-application.md index 3e121a4..2f1587c 100644 --- a/docs/book/v5/installation/running-application.md +++ b/docs/book/v5/installation/running-application.md @@ -1,5 +1,12 @@ # Running the application +## Summary + +This page runs Dotkernel Frontend in a virtual host on WSL. +It also covers the two local-only fixes: clearing a stale configuration cache and turning off secure session cookies. + +## Details + We recommend running your applications in WSL: - Make sure you have [WSL](https://github.com/dotkernel/development/blob/main/wsl/README.md) installed on your system. @@ -32,3 +39,25 @@ return [ ``` Do not change this in `local.php.dist` as well because this value should remain `true` on production. + +## FAQ + +### **Q: Which address does the application use?** + +A: The `$baseUrl` value in `config/autoload/local.php`, which is `http://dotkernel.local` by default. +Set it to the address of your virtual host. + +### **Q: Can I run Frontend without a virtual host?** + +A: Yes, for a quick look. `composer serve` starts PHP's built-in server on port 8080 (`php -S 0.0.0.0:8080 -t public/`). +Set `$baseUrl` to match. + +### **Q: Why am I logged out after every request on my local machine?** + +A: `session_config` => `cookie_secure` is `true`, so the browser sends the session cookie only over HTTPS. +Set it to `false` in your `local.php` when you develop over plain HTTP. + +### **Q: Why do my configuration changes have no effect?** + +A: A configuration cache is being loaded. +Enable development mode, or run `php bin/clear-config-cache.php`. diff --git a/docs/book/v5/introduction/file-structure.md b/docs/book/v5/introduction/file-structure.md index b6b1c03..6a9b36e 100644 --- a/docs/book/v5/introduction/file-structure.md +++ b/docs/book/v5/introduction/file-structure.md @@ -1,5 +1,12 @@ # File structure +## Summary + +This page describes the folders and files installed with Dotkernel Frontend, from `bin`, `config`, `data`, `log` and `public` to the modules in `src`. +It also describes what each module folder is expected to contain. + +## Details + Dotkernel Frontend follows the [PSR-4](https://www.php-fig.org/psr/psr-4/) standards. It is considered good practice to standardize the file structure of projects. @@ -11,13 +18,14 @@ When using Dotkernel Frontend the following structure is installed by default: ## Special purpose folders * `.github` - Contains workflow files -* `.laminas-ci` - Contains laminas-ci workflow files +* `.laminas-ci` - Contains `pre-run.sh`, which prepares the laminas-ci environment (installs sqlite3, copies the local config files) before the PHPUnit jobs ## `bin` folder This folder contents are -* `clear-config-cache.php` - Removes the config cache file `data/cache/config-cache.php`; available only when development mode is enabled +* `clear-config-cache.php` - Removes the config cache file `data/cache/config-cache.php`; also available as `composer clear-config-cache` +* `composer-post-install-script.php` - Runs after `composer install`/`composer update` and copies `local.php.dist`, `local.test.php.dist` (development only) and dot-mail's `mail.global.php.dist` into `config/autoload` when they are missing * `doctrine` - Used by the doctrine fixtures to populate the database tables * `doctrine-migrations` - Used to create the database tables @@ -25,12 +33,13 @@ This folder contents are This folder contains all application-related config files: -* `cli-config.php` - Command line interface configuration used by migrations, fixtures, crons +* `cli-config.php` - Command line interface configuration used by Doctrine Migrations * `config.php` - Registers ConfigProviders for installing packages * `container.php` - Main service container that provides access to all registered services * `development.config.php.dist` - Activates debug mode; gets symlinked as `development.config.php` when enabling development mode * `migrations.php` - Configuration for database migration, like migration file location and table to save the migration log * `pipeline.php` - Contains a list of middlewares, in the order of their execution +* `routes.php` - Application-level route registration; empty by default, as each module registers its own routes in its `RoutesDelegator` * `twig-cs-fixer.php` - Configuration file for Twig code style checker/fixer ### `config/autoload` folder @@ -48,8 +57,9 @@ This folder contains all service-related local and global config files: * `error-handling.global.php` - Configures and activates error logs * `local.php.dist` - Local config file where you can overwrite application name and URL * `local.test.php.dist` - Local configuration for functional tests -* `mail.local.php.dist` - Mail configuration; e.g. sendmail vs smtp, message configuration, mail logging +* `mail.global.php` - Mail configuration defaults (sendmail vs esmtp, message options, SMTP options); not shipped in the repository, it is copied from `vendor/dotkernel/dot-mail/config/mail.global.php.dist` by `bin/composer-post-install-script.php`; it is not ignored by git, so keep SMTP credentials in a `mail.local.php` instead * `mezzio.global.php` - Mezzio core config file +* `navigation.global.php` - dotkernel/dot-navigation menu containers (`left_menu`, `guest_menu`, `user_menu`, `user_profile_menu`) * `response-header.global.php` - Defines headers per route * `session.global.php` - Configures the session * `templates.global.php` - dotkernel/dot-twigrenderer config file @@ -59,9 +69,8 @@ This folder contains all service-related local and global config files: This folder is a storage for project data files and service caches. It contains these folders: -* `cache` - Cache for e.g. Twig files +* `cache` - Created at runtime and ignored by git; holds the config cache, the Doctrine cache and compiled Twig templates * `doctrine` - Database migrations and fixtures -* `lock` - Contains lock files generated by [`dotkernel/dot-cli`](https://docs.dotkernel.org/dot-cli/v3/lock-files/) > AVOID storing sensitive data on the repository! @@ -76,7 +85,7 @@ This folder contains all publicly available assets and serves as the entry point * `css` and `js` - Contains the css and js file(s) generated by the webpack (npm) from the assets folder * `fonts` and `images` - Contain the font and image file(s) copied by the webpack (npm) from the assets folder -* `uploads` - a folder that normally contains admin avatar images +* `uploads` - contains user avatar images, stored under `uploads/user/{userUuid}/` * `.htaccess` - server configuration file used by Apache web server; it enables the URL rewrite functionality * `index.php` - the application's main entry point * `robots.txt.dist` - a sample robots.txt file that allows/denies bot access to certain areas of your application; activate it by duplicating the file as `robots.txt` and comment out the lines that don't match your environment @@ -103,7 +112,9 @@ Each Module folder, in turn, should contain the following folders, unless they a * `src/Service` - Service classes The above example is just some of the folders a project may include, but they should give you an idea about the recommended structure. -Other classes the `src` folder may include are `InputFilter`, `EventListener`, `Helper`, `Command`, `Factory` etc. +Other folders the `src` folder may include are `Form`, `Fieldset`, `InputFilter`, `EventListener`, `Factory`, `Middleware`, `Enum`, `DBAL` etc. + +The `App` module also contains an `assets` folder (fonts, images, js, scss), which webpack compiles into `public`. The `src` folder in each Module folder normally also contains these files: @@ -116,3 +127,29 @@ This directory contains the template files. > `twig` is used as Templating Engine. > All template files have the extension `.html.twig`. + +## FAQ + +### **Q: Where do I register a new route?** + +A: In the module's `RoutesDelegator.php`. +`config/routes.php` is empty by default, because each module registers its own routes. + +### **Q: Where should I put passwords and other local settings?** + +A: In `config/autoload/local.php` or another `config/autoload/*.local.php` file. +`config/autoload/.gitignore` ignores `local.php` and `*.local.php`, so they are never committed. + +### **Q: Where are uploaded avatars stored?** + +A: In `public/uploads/user/{userUuid}/`, as set by the `uploads` key in `config/autoload/local.php`. + +### **Q: Where do I edit the CSS and JavaScript?** + +A: In `src/App/assets`. +Webpack compiles them into `public/css` and `public/js` when you run `npm run watch` or `npm run prod`. + +### **Q: How do I add a new module?** + +A: Create `src//src` with a `ConfigProvider.php` (and a `RoutesDelegator.php` if it has routes). +Add a `Frontend\\\\` PSR-4 entry to `composer.json`, register the `ConfigProvider` in `config/config.php`, and run `composer dump-autoload`. diff --git a/docs/book/v5/introduction/introduction.md b/docs/book/v5/introduction/introduction.md index 0be1596..158c02f 100644 --- a/docs/book/v5/introduction/introduction.md +++ b/docs/book/v5/introduction/introduction.md @@ -1,5 +1,13 @@ # Introduction +## Summary + +Dotkernel Frontend is a Mezzio and Laminas skeleton for server-rendered web applications with user accounts. +It ships a contact page, generic content pages and user accounts as working examples of its file architecture. +This page also links to the rest of the documentation, the live demo and the project status badges. + +## Details + Dotkernel Frontend is an application (skeleton) based on Mezzio microframework using Laminas components. It's designed as a web starter package suitable for frontend applications. The current functionality is included as a proof of concept and to showcase Frontend's file architecture: @@ -19,7 +27,7 @@ Read on to find out more about the application: Check out our [demo](https://v5.dotkernel.net/). ![OSS Lifecycle](https://img.shields.io/osslifecycle/dotkernel/frontend) -![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/frontend/4.2.0) +![Packagist Dependency Version](https://img.shields.io/packagist/dependency-v/dotkernel/frontend/php) [![GitHub issues](https://img.shields.io/github/issues/dotkernel/frontend)](https://github.com/dotkernel/frontend/issues) [![GitHub forks](https://img.shields.io/github/forks/dotkernel/frontend)](https://github.com/dotkernel/frontend/network) @@ -29,3 +37,24 @@ Check out our [demo](https://v5.dotkernel.net/). [![Continuous Integration](https://github.com/dotkernel/frontend/actions/workflows/continuous-integration.yml/badge.svg?branch=5.0)](https://github.com/dotkernel/frontend/actions/workflows/continuous-integration.yml) [![codecov](https://codecov.io/gh/dotkernel/frontend/graph/badge.svg?token=BQS43UWAM4)](https://codecov.io/gh/dotkernel/frontend) [![Qodana](https://github.com/dotkernel/frontend/actions/workflows/qodana_code_quality.yml/badge.svg)](https://github.com/dotkernel/frontend/actions/workflows/qodana_code_quality.yml) +[![PHPStan](https://github.com/dotkernel/frontend/actions/workflows/static-analysis.yml/badge.svg?branch=5.0)](https://github.com/dotkernel/frontend/actions/workflows/static-analysis.yml) + +## FAQ + +### **Q: Is Dotkernel Frontend a finished product?** + +A: No. The contact page, the content pages and the user accounts are a proof of concept that shows where your own code goes. +Use them as examples, and change or remove them as your application needs. + +### **Q: Which version of Frontend does this documentation cover?** + +A: Version 5, which is the `5.0` branch of `dotkernel/frontend`. + +### **Q: Does Frontend use request handlers or controllers?** + +A: Action controllers. +Each controller extends `Dot\Controller\AbstractActionController`, and the `{action}` route parameter selects the method to run, for example `/user/login` runs `UserController::loginAction()`. + +### **Q: Where can I see Frontend running?** + +A: The demo is at https://v5.dotkernel.net/. diff --git a/docs/book/v5/introduction/packages.md b/docs/book/v5/introduction/packages.md index 3fd9c2c..400cd10 100644 --- a/docs/book/v5/introduction/packages.md +++ b/docs/book/v5/introduction/packages.md @@ -1,6 +1,13 @@ # Packages +## Summary + +This page lists the Composer packages that Dotkernel Frontend requires directly, with a one-line description of what each one provides. + +## Details + * `dotkernel/dot-authorization` - Authorization base package defining interfaces for authorization services to be used with Dotkernel applications +* `dotkernel/dot-cache` - Cache adapters used by Doctrine for metadata, query, result and hydration caching * `dotkernel/dot-controller` - Provides base classes for action based controllers similar to Laminas controller component * `dotkernel/dot-data-fixtures` - Provides a CLI interface for listing & executing doctrine data fixtures * `dotkernel/dot-dependency-injection` - Dependency injection component using class attributes @@ -23,3 +30,23 @@ * `mezzio/mezzio-fastroute` - FastRoute integration for Mezzio * `ramsey/uuid-doctrine` - Use ramsey/uuid as a Doctrine field type * `roave/psr-container-doctrine` - Doctrine Factories for PSR-11 Containers + +## FAQ + +### **Q: Where is the authoritative list of dependencies?** + +A: In the `require` section of `composer.json`. +Development tools such as PHPUnit, PHPStan, the Laminas coding standard, Twig CS Fixer, Whoops and laminas-development-mode are in `require-dev`. + +### **Q: Which package handles routing?** + +A: `mezzio/mezzio-fastroute`, the FastRoute integration for Mezzio. + +### **Q: Which templating engine does Frontend use?** + +A: Twig, through `mezzio/mezzio-twigrenderer`, with Dotkernel's extensions from `dotkernel/dot-twigrenderer`. + +### **Q: Which packages control access to pages?** + +A: `dotkernel/dot-rbac-guard` applies the guards, and `dotkernel/dot-authorization` and `mezzio/mezzio-authorization-rbac` provide the role and permission model. +See [Authorization Guards](../how-to/authorization.md). diff --git a/docs/book/v5/introduction/server-requirements.md b/docs/book/v5/introduction/server-requirements.md index 8c4e054..79dba5c 100644 --- a/docs/book/v5/introduction/server-requirements.md +++ b/docs/book/v5/introduction/server-requirements.md @@ -1,5 +1,12 @@ # Server Requirements +## Summary + +This page lists what a server needs to run Dotkernel Frontend: a web server (Apache or Nginx), PHP with its required settings and extensions, and a MariaDB or MySQL database. +It also lists the extensions that are recommended for common tasks and for running the tests. + +## Details + For production, we highly recommend a *nix based system. ## Webserver @@ -15,7 +22,7 @@ For production, we highly recommend a *nix based system. You need to convert the provided Apache related `.htaccess` file into Nginx configuration instructions. -## PHP >= 8.2 +## PHP 8.2 or 8.3 Both mod_php and FCGI (FPM) are supported. @@ -23,6 +30,10 @@ Both mod_php and FCGI (FPM) are supported. * memory_limit >= 128M * upload_max_filesize and post_max_size >= 100M (depending on your data) +* curl (used by the contact form's reCAPTCHA verification) +* gettext +* intl (required by `laminas/laminas-i18n`) +* json * mbstring * CLI SAPI (for Cron Jobs) * Composer (added to $PATH) @@ -41,10 +52,29 @@ mysql_native_password=ON ## Recommended extensions * opcache -* pdo_mysql or mysqli (if using MySQL or MariaDB as RDBMS) +* pdo_mysql (the driver configured in `config/autoload/local.php.dist` for MySQL or MariaDB) * dom - if working with markup files structure (html, xml etc.) * simplexml - working with xml files * gd, exif - if working with images * zlib, zip, bz2 - if compressing files -* curl (required if APIs are used) * sqlite3 - for tests + +## FAQ + +### **Q: Which PHP versions are supported?** + +A: PHP 8.2 and 8.3; `composer.json` requires `~8.2.0 || ~8.3.0`. + +### **Q: Can I use Nginx instead of Apache?** + +A: Yes. +Convert the rewrite rules in `public/.htaccess` into Nginx configuration so that every request for a file that does not exist is sent to `public/index.php`. + +### **Q: Can I use PostgreSQL?** + +A: Not out of the box. +`config/autoload/local.php.dist` configures the `pdo_mysql` driver, and the shipped migration uses MySQL/MariaDB syntax such as `ENUM` columns. + +### **Q: Why is `sqlite3` needed for tests?** + +A: The test configuration in `config/autoload/local.test.php.dist` connects Doctrine to an in-memory SQLite database (`sqlite3:///:memory:`). diff --git a/docs/book/v5/reference/account-anonymization.md b/docs/book/v5/reference/account-anonymization.md index d98460a..0818ee0 100644 --- a/docs/book/v5/reference/account-anonymization.md +++ b/docs/book/v5/reference/account-anonymization.md @@ -1,5 +1,9 @@ # Account anonymization +## Summary + +This page explains why Dotkernel Frontend anonymizes user accounts instead of deleting them, which personal data it stores, and exactly what the anonymization process replaces. + ## Premise According to the GDPR, companies that record personal data from EU citizens must delete said data if its owner requests its deletion. @@ -25,16 +29,43 @@ According to [this article](https://commission.europa.eu/law/law-topic/data-prot Out of the box, Dotkernel Frontend saves the user's name (firstname and lastname) and email (identity). This personal data is used for emails related to password reset and account activation. +> Other tables can also hold personal data: `contact_message` stores the name and email of each contact form sender, `user_remember_me` stores the browser user agent, and `user_avatar` references an uploaded image. +> Anonymization does not change `contact_message` or `user_remember_me` records. + ## Process ### Anonymization -The anonymization process makes these replacements: +Anonymization runs when a logged-in user deletes their account (`/account/delete-account`), and when a pending account is unregistered through the link in the activation email (`/account/unregister/{hash}`). +The user record is kept, its status is set to `deleted`, and the process makes these replacements: -- The firstname and lastname are replaced with `anonymous` concatenated with the current UNIX timestamp, e.g. `anonymous1725980747`. -- The email is replaced with `anonymous` concatenated with the current UNIX timestamp and the value in `userAnonymizeAppend`, e.g. `anonymous1725980747@example.com`. -- The avatar image and its database record are deleted. +- The firstname and lastname are replaced with `anonymous` concatenated with the current date and time in `dmYHis` format, e.g. `anonymous23092026155300`. +- The email is replaced with the same value concatenated with the value in `userAnonymizeAppend`, e.g. `anonymous23092026155300@example.com`. +- On account deletion, the avatar image, its upload folder and its database record are deleted. The `userAnonymizeAppend` key can be set in `config/autoload/local.php` or left empty. > Using an email domain for `userAnonymizeAppend` would work as a catch-all email, if your email service provider has this option enabled. + +## FAQ + +### **Q: Is the user record deleted?** + +A: No. The record is kept, its status is set to `deleted`, and the name and email are replaced. + +### **Q: Can an anonymized user log in again?** + +A: No. Login accepts only accounts whose status is `active`, as set in `config/autoload/authentication.global.php`. + +### **Q: What happens if `userAnonymizeAppend` is empty?** + +A: The email is replaced by the placeholder alone, for example `anonymous23092026155300`, which is not a valid email address. + +### **Q: Are contact form messages anonymized as well?** + +A: No. Rows in `contact_message` keep the sender's name and email. + +### **Q: Can two anonymized accounts clash?** + +A: Yes, if both are anonymized in the same second. +The placeholder only has one-second resolution and `user.identity` is unique, so the second account fails to save.