aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2023-03-04 13:30:45 +0100
committerGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2023-03-04 13:30:45 +0100
commitb3239256dc6d188cda970adab516b3fcf1b86129 (patch)
treed8e65dd9784834ba2e82ce7ee94b4718f8af19ea /docs
parent27b71ffa99f7dff013fb8d51d020ed628e0d2ce6 (diff)
parent0fe0ce894cbad09757d719dd4b400b9862c1a12a (diff)
Merge branch 'edge' into latest
Diffstat (limited to 'docs')
-rw-r--r--docs/en/admins/02_Prerequisites.md2
-rw-r--r--docs/en/admins/05_Configuring_email_validation.md2
-rw-r--r--docs/en/admins/06_LinuxInstall.md9
-rw-r--r--docs/en/admins/07_LinuxUpdate.md12
-rw-r--r--docs/en/admins/08_FeedUpdates.md2
-rw-r--r--docs/en/admins/10_ServerConfig.md6
-rw-r--r--docs/en/developers/02_Github.md2
-rw-r--r--docs/en/developers/03_Backend/05_Extensions.md190
-rw-r--r--docs/en/developers/Minz/index.md198
-rw-r--r--docs/en/developers/OPML.md6
-rw-r--r--docs/en/internationalization.md2
-rw-r--r--docs/en/users/02_First_steps.md6
-rw-r--r--docs/en/users/03_Main_view.md261
-rw-r--r--docs/en/users/04_Subscriptions.md4
-rw-r--r--docs/en/users/05_Configuration.md27
-rw-r--r--docs/en/users/09_refreshing_feeds.md134
-rw-r--r--docs/en/users/10_filter.md152
-rw-r--r--docs/fr/developers/01_First_steps.md2
-rw-r--r--docs/fr/developers/03_Backend/02_Minz.md29
-rw-r--r--docs/fr/developers/03_Backend/05_Extensions.md324
-rw-r--r--docs/fr/developers/Minz/index.md249
-rw-r--r--docs/fr/developers/Minz/migration.md3
-rw-r--r--docs/fr/internationalization.md79
-rw-r--r--docs/fr/users/01_Installation.md2
-rw-r--r--docs/fr/users/03_Main_view.md2
-rw-r--r--docs/fr/users/05_Configuration.md27
26 files changed, 883 insertions, 849 deletions
diff --git a/docs/en/admins/02_Prerequisites.md b/docs/en/admins/02_Prerequisites.md
index 41dabc651..c2a2b3fa7 100644
--- a/docs/en/admins/02_Prerequisites.md
+++ b/docs/en/admins/02_Prerequisites.md
@@ -7,7 +7,7 @@ You need to verify that your server can run FreshRSS before installing it. If yo
| Software | Recommended | Also Works With |
| ------------- | ----------------------- | ----------------------- |
| Web server | **Apache 2** | Nginx, lighttpd |
-| PHP | **PHP 7+** | |
+| PHP | **PHP 7.2+** | |
| PHP modules | Required: libxml, cURL, JSON, PDO_MySQL, PCRE and ctype.<br />Required (32-bit only): GMP <br />Recommended: Zlib, mbstring, iconv, ZipArchive<br />*For the whole modules list see [Dockerfile](https://github.com/FreshRSS/FreshRSS/blob/edge/Docker/Dockerfile-Alpine#L7-L9)* | |
| Database | **MySQL 5.5.3+** | SQLite 3.7.4+, PostgreSQL 9.5+ |
| Browser | **Firefox** | Chrome, Opera, Safari, or Edge |
diff --git a/docs/en/admins/05_Configuring_email_validation.md b/docs/en/admins/05_Configuring_email_validation.md
index 69a06b53f..aec6b8b75 100644
--- a/docs/en/admins/05_Configuring_email_validation.md
+++ b/docs/en/admins/05_Configuring_email_validation.md
@@ -68,7 +68,7 @@ Once you’re done, don’t forget to reconfigure your environment to `productio
## Access the validation URL during development
-You might find painful to configure a SMTP server when you’re developping and
+You might find painful to configure a SMTP server when you’re developing and
`mail` function will not work on your local machine. For the moment, there is
no easy way to access the validation URL unless forging it. You’ll need to
information:
diff --git a/docs/en/admins/06_LinuxInstall.md b/docs/en/admins/06_LinuxInstall.md
index e92fc3247..1af041efe 100644
--- a/docs/en/admins/06_LinuxInstall.md
+++ b/docs/en/admins/06_LinuxInstall.md
@@ -81,14 +81,7 @@ Change to the new FreshRSS directory, and set the permissions so that your Web s
```sh
cd FreshRSS
-chown -R :www-data .
-sudo chmod -R g+r .
-```
-
-We’ll also need to allow the data folder to be written to, like so:
-
-```sh
-chmod -R g+w ./data/
+sudo cli/access-permissions.sh
```
Optional: If you would like to allow updates from the Web interface, set write permissions
diff --git a/docs/en/admins/07_LinuxUpdate.md b/docs/en/admins/07_LinuxUpdate.md
index 834dfaaef..47b84e5ef 100644
--- a/docs/en/admins/07_LinuxUpdate.md
+++ b/docs/en/admins/07_LinuxUpdate.md
@@ -49,22 +49,22 @@ If your local user doesn’t have write access to the FreshRSS folder, use a sud
4. Update FreshRSS
```sh
git checkout edge
- git pull
- git checkout $(git describe --tags --abbrev=0)
+ git pull --ff-only
```
- Note: If you want to use the rolling release, the last command is optional.
+ > ℹ️ Use `edge` for the rolling release or `latest` for the latest stable release.
5. (optional) Make sure you use the correct version
```sh
git status
```
- The command should tell you the tag that you’re using. It must be the same as the one associated with [the latest release on GitHub](https://github.com/FreshRSS/FreshRSS/releases/latest). If you use the rolling release, it should tell you that your `edge` branch is up to date with `origin`.
+ The command should tell you the branch that you’re using. It must be the same as the one associated with [the latest release on GitHub](https://github.com/FreshRSS/FreshRSS/releases/latest).
+ If you use the rolling release, it should tell you that your `edge` branch is up to date with `origin`.
6. Re-set correct permissions so that your web server can access the files
```sh
- chown -R :www-data . && chmod -R g+r . && chmod -R g+w ./data/
+ cli/access-permissions.sh
```
## Using the Zip archive
@@ -91,7 +91,7 @@ If your local user doesn’t have write access to the FreshRSS folder, use a sud
5. Re-set permissions
```sh
- chown -R :www-data . && chmod -R g+r . && chmod -R g+w ./data/
+ cli/access-permissions.sh
```
6. Clean up the FreshRSS directory by deleting the downloaded zip and the temporary directory
diff --git a/docs/en/admins/08_FeedUpdates.md b/docs/en/admins/08_FeedUpdates.md
index 2072188bd..79c2bbea6 100644
--- a/docs/en/admins/08_FeedUpdates.md
+++ b/docs/en/admins/08_FeedUpdates.md
@@ -6,7 +6,7 @@ FreshRSS is updated by the `./app/actualize_script.php` script. Knowing this, we
**Note:** the following examples assume that FreshRSS is installed to `/usr/share/FreshRSS`. You’ll need to modify the FreshRSS path to reflect your own system.
-**Note:** If you cannot configure a local Cronjob, [see an alternative using online cron](../users/03_Main_view.md#online-cron).
+**Note:** If you cannot configure a local Cronjob, [see an alternative using online cron](../users/09_refreshing_feeds.md#online-cron).
## Cron as a trigger
diff --git a/docs/en/admins/10_ServerConfig.md b/docs/en/admins/10_ServerConfig.md
index 6c5823a2d..87bd74d09 100644
--- a/docs/en/admins/10_ServerConfig.md
+++ b/docs/en/admins/10_ServerConfig.md
@@ -1,8 +1,13 @@
# Apache/Nginx Configuration Files
+> ℹ️ For improved security, remove sensitive information in the Web server logs by using our [`sensitive-log.sh` script](https://github.com/FreshRSS/FreshRSS/blob/edge/cli/sensitive-log.sh),
+on the model of our [reference Apache configuration](https://github.com/FreshRSS/FreshRSS/blob/edge/Docker/FreshRSS.Apache.conf) used for our official Docker images
+(see [`CustomLog`](https://httpd.apache.org/docs/current/mod/mod_log_config.html#customlog)).
+
## Apache configuration
This is an example Apache virtual hosts configuration file. It covers HTTP and HTTPS configuration.
+For more details, check our [reference Apache configuration](https://github.com/FreshRSS/FreshRSS/blob/edge/Docker/FreshRSS.Apache.conf) used for our official Docker images.
```apache
<VirtualHost *:80>
@@ -24,6 +29,7 @@ This is an example Apache virtual hosts configuration file. It covers HTTP and H
</Directory>
ErrorLog ${APACHE_LOG_DIR}/freshrss_error.log
+ # Consider piping the logs for cleaning passwords; cf. comment higher up.
CustomLog ${APACHE_LOG_DIR}/freshrss_access.log combined
AllowEncodedSlashes On
diff --git a/docs/en/developers/02_Github.md b/docs/en/developers/02_Github.md
index 4e6a84ab3..066d6ffb0 100644
--- a/docs/en/developers/02_Github.md
+++ b/docs/en/developers/02_Github.md
@@ -53,7 +53,7 @@ Now you can create a PR based on your branch.
## How to write a commit message
-A commit message should succintly describe the changes on the first line. For example:
+A commit message should succinctly describe the changes on the first line. For example:
> Fix broken icon
diff --git a/docs/en/developers/03_Backend/05_Extensions.md b/docs/en/developers/03_Backend/05_Extensions.md
index aa707d2e4..e86d73bfa 100644
--- a/docs/en/developers/03_Backend/05_Extensions.md
+++ b/docs/en/developers/03_Backend/05_Extensions.md
@@ -22,195 +22,9 @@ Another solution consists of an extension system. By allowing users to write the
Note: it is quite conceivable that the functionalities of an extension can later be officially integrated into the FreshRSS code. Extensions make it easy to propose a proof of concept.
-## Understanding basic mechanics (Minz and MVC)
+## Minz Framework
-**TODO** : move to 02_Minz.md
-
-This data sheet should refer to the official FreshRSS and Minz documentation (the PHP framework on which FreshRSS is based). Unfortunately, this documentation does not yet exist. In a few words, here are the main things you should know. It is not necessary to read all the chapters in this section if you don’t need to use a feature in your extension (if you don’t need to translate your extension, no need to know more about the `Minz_Translate` module for example).
-
-### MVC Architecture
-
-Minz relies on and imposes an MVC architecture on projects using it. This architecture consists of three main components:
-
-* The model: this is the base object that we will manipulate. In FreshRSS, categories, flows and articles are templates. The part of the code that makes it possible to manipulate them in a database is also part of the model but is separated from the base model: we speak of DAO (for "Data Access Object"). The templates are stored in a `Models` folder.
-* The view: this is what the user sees. The view is therefore simply HTML code mixed with PHP to display dynamic information. The views are stored in a `views` folder.
-* The controller: this is what makes it possible to link models and views. Typically, a controller will load templates from the database (like a list of items) to "pass" them to a view for display. Controllers are stored in a `Controllers` directory.
-
-### Routing
-
-In order to link a URL to a controller, first you have to go through a "routing" phase. In FreshRSS, this is particularly simple because it suffices to specify the name of the controller to load into the URL using a `c` parameter.
-For example, the address <http://example.com?c=hello> will execute the code contained in the `hello` controller.
-
-One concept that has not yet been discussed is the "actions" system. An action is executed *on* a controller. Concretely, a controller is represented by a class and its actions by methods. To execute an action, it is necessary to specify an `a` parameter in the URL.
-
-Code example:
-
-```php
-<?php
-
-class FreshRSS_hello_Controller extends FreshRSS_ActionController {
- public function indexAction() {
- $this->view->a_variable = 'FooBar';
- }
-
- public function worldAction() {
- $this->view->a_variable = 'Hello World!';
- }
-}
-
-?>
-```
-
-When loading the address <http://example.com?c=hello&a=world>, the `world` action is executed on the `hello` controller.
-
-Note: if `c` or `a` is not specified, the default value for each of these variables is `index`.
-So the address <http://example.com?c=hello> will execute the `index` action of the `hello` controller.
-
-From now on, the `hello/world` naming convention will be used to refer to a controller/action pair.
-
-### Views
-
-Each view is associated with a controller and an action. The view associated with `hello/world` will be stored in a very specific file: `views/hello/world. phtml`. This convention is imposed by Minz.
-
-As explained above, the views consist of HTML mixed with PHP. Code example:
-
-```html
-<p>
- This is a parameter passed from the controller: <?= $this->a_variable ?>
-</p>
-```
-
-The variable `$this->a_variable` is passed by the controller (see previous example). The difference is that in the controller it is necessary to pass `$this->view`, while in the view `$this` suffices.
-
-### Working with GET / POST
-
-It is often necessary to take advantage of parameters passed by GET or POST. In Minz, these parameters are accessible using the `Minz_Request` class.
-Code example:
-
-```php
-<?php
-
-$default_value = 'foo';
-$param = Minz_Request::param('bar', $default_value);
-
-// Display the value of the parameter `bar` (passed via GET or POST)
-// or "foo" if the parameter does not exist.
-echo $param;
-
-// Sets the value of the `bar` parameter
-Minz_Request::_param('bar', 'baz');
-
-// Will necessarily display "baz" since we have just forced its value.
-// Note that the second parameter (default) is optional.
-echo Minz_Request::param('bar');
-
-?>
-```
-
-The `Minz_Request::isPost()` method can be used to execute a piece of code only if it is a POST request.
-
-Note: it is preferable to use `Minz_Request` only in controllers. It is likely that you will encounter this method in FreshRSS views, or even in templates, but be aware that this is **not** good practice.
-
-### Access session settings
-
-The access to session parameters is strangely similar to the GET / POST parameters but passes through the `Minz_Session` class this time! There is no example here because you can repeat the previous example by changing all `Minz_Request` to `Minz_Session`.
-
-### Working with URLs
-
-To take full advantage of the Minz routing system, it is strongly discouraged to write hard URLs in your code. For example, the following view should be avoided:
-
-```html
-<p>
- Go to page <a href="http://example.com?c=hello&amp;a=world">Hello world</a>!
-</p>
-```
-
-If one day it was decided to use a "url rewriting" system to have addresses in a <http://example.com/controller/action> format, all previous addresses would become ineffective!
-
-So use the `Minz_Url` class and its `display()` method instead. `Minz_Url::display()` takes an array of the following form as its argument:
-
-```php
-<?php
-
-$url_array = [
- 'c' => 'hello',
- 'a' => 'world',
- 'params' => [
- 'foo' => 'bar',
- ],
-];
-
-// Show something like .?c=hello&amp;a=world&amp;foo=bar
-echo Minz_Url::display($url_array);
-
-?>
-```
-
-Since this can become a bit tedious to use in the long run, especially in views, it is preferable to use the `_url()` shortcut:
-
-```php
-<?php
-
-// Displays the same as above
-echo _url('hello', 'world', 'foo', 'bar');
-
-?>
-```
-
-Note: as a general rule, the shortened form (`_url()`) should be used in views, while the long form (`Minz_Url::display()`) should be used in controllers.
-
-### Redirections
-
-It is often necessary to redirect a user to another page. To do so, the `Minz_Request` class offers another useful method: `forward()`. This method takes the same URL format as the one seen just before as its argument.
-
-Code example:
-
-```php
-<?php
-
-$url_array = [
- 'c' => 'hello',
- 'a' => 'world',
-];
-
-// Tells Minz to redirect the user to the hello / world page.
-// Note that this is a redirection in the Minz sense of the term, not a redirection that the browser will have to manage (HTTP code 301 or 302)
-// The code that follows forward() will thus be executed!
-Minz_Request::forward($url_array);
-
-// To perform a type 302 redirect, add "true".
-// The code that follows will never be executed.
-Minz_Request::forward($url_array, true);
-
-?>
-```
-
-It is very common to want display a message to the user while performing a redirect, to tell the user how the action was carried out (validation of a form for example). Such a message is passed through a `notification` session variable (note: we will talk about feedback from now on to avoid confusion with a notification that can occur at any time). To facilitate this kind of very frequent action, there are two shortcuts that both perform a 302 redirect by assigning a feedback message:
-
-```php
-<?php
-
-$url_array = [
- 'c' => 'hello',
- 'a' => 'world',
-];
-$feedback_good = 'All went well!';
-$feedback_bad = 'Oops, something went wrong.';
-
-Minz_Request::good($feedback_good, $url_array);
-
-// or
-
-Minz_Request::bad($feedback_bad, $url_array);
-
-?>
-```
-
-### Translation Management
-
-This part [is explained here](/docs/en/internationalization.md).
-
-### Configuration management
+see [Minz documentation](/docs/en/developers/Minz/index.md)
## Write an extension for FreshRSS
diff --git a/docs/en/developers/Minz/index.md b/docs/en/developers/Minz/index.md
index 9b6d46f17..ed5bc0482 100644
--- a/docs/en/developers/Minz/index.md
+++ b/docs/en/developers/Minz/index.md
@@ -1,19 +1,193 @@
-# Minz
+# Minz Framework
Minz is the homemade PHP framework used by FreshRSS.
-The documentation is still incomplete and it would be great to explain:
+This data sheet should refer to the official FreshRSS and Minz documentation (the PHP framework on which FreshRSS is based). Unfortunately, this documentation does not yet exist. In a few words, here are the main things you should know. It is not necessary to read all the chapters in this section if you don’t need to use a feature in your extension (if you don’t need to translate your extension, no need to know more about the `Minz_Translate` module for example).
-- routing, controllers and actions
-- configuration
-- models and database
-- views
-- URLs management
-- sessions
-- internationalisation
-- extensions
-- mailer
+## MVC Architecture
+
+Minz relies on and imposes an MVC architecture on projects using it. This architecture consists of three main components:
+
+* The model: this is the base object that we will manipulate. In FreshRSS, categories, flows and articles are templates. The part of the code that makes it possible to manipulate them in a database is also part of the model but is separated from the base model: we speak of DAO (for "Data Access Object"). The templates are stored in a `Models` folder.
+* The view: this is what the user sees. The view is therefore simply HTML code mixed with PHP to display dynamic information. The views are stored in a `views` folder.
+* The controller: this is what makes it possible to link models and views. Typically, a controller will load templates from the database (like a list of items) to "pass" them to a view for display. Controllers are stored in a `Controllers` directory.
+
+## Routing
+
+In order to link a URL to a controller, first you have to go through a "routing" phase. In FreshRSS, this is particularly simple because it suffices to specify the name of the controller to load into the URL using a `c` parameter.
+For example, the address <http://example.com?c=hello> will execute the code contained in the `hello` controller.
+
+One concept that has not yet been discussed is the "actions" system. An action is executed *on* a controller. Concretely, a controller is represented by a class and its actions by methods. To execute an action, it is necessary to specify an `a` parameter in the URL.
+
+Code example:
+
+```php
+<?php
+
+class FreshRSS_hello_Controller extends FreshRSS_ActionController {
+ public function indexAction() {
+ $this->view->a_variable = 'FooBar';
+ }
+
+ public function worldAction() {
+ $this->view->a_variable = 'Hello World!';
+ }
+}
+
+?>
+```
+
+When loading the address <http://example.com?c=hello&a=world>, the `world` action is executed on the `hello` controller.
+
+Note: if `c` or `a` is not specified, the default value for each of these variables is `index`.
+So the address <http://example.com?c=hello> will execute the `index` action of the `hello` controller.
+
+From now on, the `hello/world` naming convention will be used to refer to a controller/action pair.
+
+## Views
+
+Each view is associated with a controller and an action. The view associated with `hello/world` will be stored in a very specific file: `views/hello/world. phtml`. This convention is imposed by Minz.
+
+As explained above, the views consist of HTML mixed with PHP. Code example:
+
+```html
+<p>
+ This is a parameter passed from the controller: <?= $this->a_variable ?>
+</p>
+```
+
+The variable `$this->a_variable` is passed by the controller (see previous example). The difference is that in the controller it is necessary to pass `$this->view`, while in the view `$this` suffices.
+
+## Working with GET / POST
+
+It is often necessary to take advantage of parameters passed by GET or POST. In Minz, these parameters are accessible using the `Minz_Request` class.
+Code example:
+
+```php
+<?php
+
+$default_value = 'foo';
+$param = Minz_Request::param('bar', $default_value);
+
+// Display the value of the parameter `bar` (passed via GET or POST)
+// or "foo" if the parameter does not exist.
+echo $param;
+
+// Sets the value of the `bar` parameter
+Minz_Request::_param('bar', 'baz');
+
+// Will necessarily display "baz" since we have just forced its value.
+// Note that the second parameter (default) is optional.
+echo Minz_Request::param('bar');
+
+?>
+```
+
+The `Minz_Request::isPost()` method can be used to execute a piece of code only if it is a POST request.
+
+Note: it is preferable to use `Minz_Request` only in controllers. It is likely that you will encounter this method in FreshRSS views, or even in templates, but be aware that this is **not** good practice.
+
+## Access session settings
+
+The access to session parameters is strangely similar to the GET / POST parameters but passes through the `Minz_Session` class this time! There is no example here because you can repeat the previous example by changing all `Minz_Request` to `Minz_Session`.
+
+## Working with URLs
+
+To take full advantage of the Minz routing system, it is strongly discouraged to write hard URLs in your code. For example, the following view should be avoided:
+
+```html
+<p>
+ Go to page <a href="http://example.com?c=hello&amp;a=world">Hello world</a>!
+</p>
+```
+
+If one day it was decided to use a "url rewriting" system to have addresses in a <http://example.com/controller/action> format, all previous addresses would become ineffective!
+
+So use the `Minz_Url` class and its `display()` method instead. `Minz_Url::display()` takes an array of the following form as its argument:
+
+```php
+<?php
+
+$url_array = [
+ 'c' => 'hello',
+ 'a' => 'world',
+ 'params' => [
+ 'foo' => 'bar',
+ ],
+];
+
+// Show something like .?c=hello&amp;a=world&amp;foo=bar
+echo Minz_Url::display($url_array);
+
+?>
+```
+
+Since this can become a bit tedious to use in the long run, especially in views, it is preferable to use the `_url()` shortcut:
+
+```php
+<?php
+
+// Displays the same as above
+echo _url('hello', 'world', 'foo', 'bar');
+
+?>
+```
+
+Note: as a general rule, the shortened form (`_url()`) should be used in views, while the long form (`Minz_Url::display()`) should be used in controllers.
+
+## Redirections
+
+It is often necessary to redirect a user to another page. To do so, the `Minz_Request` class offers another useful method: `forward()`. This method takes the same URL format as the one seen just before as its argument.
+
+Code example:
+
+```php
+<?php
+
+$url_array = [
+ 'c' => 'hello',
+ 'a' => 'world',
+];
+
+// Tells Minz to redirect the user to the hello / world page.
+// Note that this is a redirection in the Minz sense of the term, not a redirection that the browser will have to manage (HTTP code 301 or 302)
+// The code that follows forward() will thus be executed!
+Minz_Request::forward($url_array);
+
+// To perform a type 302 redirect, add "true".
+// The code that follows will never be executed.
+Minz_Request::forward($url_array, true);
+
+?>
+```
+
+It is very common to want display a message to the user while performing a redirect, to tell the user how the action was carried out (validation of a form for example). Such a message is passed through a `notification` session variable (note: we will talk about feedback from now on to avoid confusion with a notification that can occur at any time). To facilitate this kind of very frequent action, there are two shortcuts that both perform a 302 redirect by assigning a feedback message:
+
+```php
+<?php
+
+$url_array = [
+ 'c' => 'hello',
+ 'a' => 'world',
+];
+$feedback_good = 'All went well!';
+$feedback_bad = 'Oops, something went wrong.';
+
+Minz_Request::good($feedback_good, $url_array);
+
+// or
+
+Minz_Request::bad($feedback_bad, $url_array);
+
+?>
+```
+
+## Translation Management
+
+This part [is explained here](/docs/en/internationalization.md).
+
+## Migration
Existing documentation includes:
-- [How to manage migrations](migrations.md)
+* [How to manage migrations](migrations.md)
diff --git a/docs/en/developers/OPML.md b/docs/en/developers/OPML.md
index 2190a1de3..5191592a8 100644
--- a/docs/en/developers/OPML.md
+++ b/docs/en/developers/OPML.md
@@ -17,17 +17,19 @@ FreshRSS uses the XML namespace <https://freshrss.org/opml> to export/import ext
The list of the custom FreshRSS attributes can be seen in [the source code](https://github.com/FreshRSS/FreshRSS/blob/edge/app/views/helpers/export/opml.phtml), and here is an overview:
-### HTML+XPath
+### HTML+XPath or XML+XPath
* `<outline type="HTML+XPath" ...`: Additional type of source, which is not RSS/Atom, but HTML Web Scraping using [XPath](https://www.w3.org/TR/xpath-10/) 1.0.
> ℹ️ [XPath 1.0](https://en.wikipedia.org/wiki/XPath) is a standard query language, which FreshRSS supports to enable [Web scraping](https://en.wikipedia.org/wiki/Web_scraping).
+* `<outline type="XML+XPath" ...`: Same than `HTML+XPath` but using an XML parser.
+
The following attributes are using similar naming conventions than [RSS-Bridge](https://rss-bridge.github.io/rss-bridge/Bridge_API/XPathAbstract.html).
* `frss:xPathItem`: XPath expression for extracting the feed items from the source page.
* Example: `//div[@class="news-item"]`
-* `frss:xPathItemTitle`: XPath expression for extracting the feed title from the source page.
+* `frss:xPathItemTitle`: XPath expression for extracting the item’s title from the item context.
* Example: `descendant::h2`
* `frss:xPathItemContent`: XPath expression for extracting an item’s content from the item context.
* Example: `.`
diff --git a/docs/en/internationalization.md b/docs/en/internationalization.md
index 3feb4f57c..741a642c7 100644
--- a/docs/en/internationalization.md
+++ b/docs/en/internationalization.md
@@ -86,7 +86,7 @@ This command adds an IGNORE comment on the translation so the key can be conside
## Add/remove/update a key
-If you’re developping a new part of the application, you might want to declare a new translation key. Your first impulse would be to add the key to each file manually: don’t do that, it’s very painful. We provide another command:
+If you’re developing a new part of the application, you might want to declare a new translation key. Your first impulse would be to add the key to each file manually: don’t do that, it’s very painful. We provide another command:
```sh
make i18n-add-key key=the.key.to.add value='Your string in English'
diff --git a/docs/en/users/02_First_steps.md b/docs/en/users/02_First_steps.md
index 3bcdaab08..7f176af77 100644
--- a/docs/en/users/02_First_steps.md
+++ b/docs/en/users/02_First_steps.md
@@ -19,9 +19,9 @@ Now that you’ve mastered basic use, it’s time to configure FreshRSS to impro
* [Organize your feeds in categories](04_Subscriptions.md#feed-management)
* [Change the home page](05_Configuration.md#changing-the-view)
* [Choose the reading options](05_Configuration.md#reading-options)
-* [Refresh feeds](03_Main_view.md#refreshing-feeds)
-* [Filter articles](03_Main_view.md#filtering-articles) for a fast access to a selection
-* [search for an article](03_Main_view.md#with-the-search-field) published some time ago
+* [Refresh feeds](09_refreshing_feeds.md)
+* [Filter articles](10_filter.md) for a fast access to a selection
+* [search for an article](10_filter.md#with-the-search-field) published some time ago
* [Access your feeds on a mobile device](06_Mobile_access.md)
* [Add some extensions](https://github.com/FreshRSS/Extensions)
* [Frequently asked questions](07_Frequently_Asked_Questions.md)
diff --git a/docs/en/users/03_Main_view.md b/docs/en/users/03_Main_view.md
index eb8fe0f01..7a0320cb6 100644
--- a/docs/en/users/03_Main_view.md
+++ b/docs/en/users/03_Main_view.md
@@ -1,10 +1,12 @@
+# Views
+
FreshRSS has three primary viewing modes: Normal, Global, and Reader view.
-# Normal view
+## Normal view
Normal view will allow you to view articles in a compressed view. They can be separated by category or individual feed, or viewed in the "main stream" containing all feeds. Clicking a feed in the sidebar (mobile users will need to click the folder icon to open it) will open that feed’s view.
-## Article List
+### Article List
By default, the normal view includes six items per article. From left to right:
* **Read status:** An envelope icon to show if the article has been read or not. Closed envelopes are unread, open envelopes are read. Clicking on the icon will toggle the read status.
@@ -14,7 +16,7 @@ By default, the normal view includes six items per article. From left to right:
* **Article date/time:** The time the article was posted.
* **Link to original article:** A globe icon that can be clicked to go to the article on the original website.
-## Normal View Sidebar
+### Normal View Sidebar
Clicking the gear icon next to an individual feed will display additional options for that feed.
* **Filter:** Run the defined filter to mark articles as read
@@ -24,256 +26,15 @@ Clicking the gear icon next to an individual feed will display additional option
* **Actualize:** Force-update the feed
* **Mark as read:** Mark all items in the feed as read
-# Global view
+## Global view
Global view allows quick views of feed’s statuses at once. Feeds and categories are shown with the number of unread articles next to them. Clicking a feed’s name will open it in a view similar to normal view.
-# Reader view
+## Reader view
Reader view will display a feed will all articles already open for reading. Feeds can be switched by clicking the folder icon at the top to bring up the category/feed sidebar.
-# Refreshing feeds
-
-To take full advantage of FreshRSS, it needs to retrieve new items from the feeds you have subscribed to. There are several ways to do this.
-
-## Automatic update with cron
-
-This is the recommended method.
-
-This method is only available if you have access to the scheduled tasks of the machine on which your FreshRSS instance is installed.
-
-The script is named *actualize_script.php* and is located in the *app* folder. The scheduled task syntax will not be explained here. However, here is [a quick introduction to crontab](http://www.adminschoice.com/crontab-quick-reference/) that might help you.
-
-Here is an example to trigger article update every hour.
-
-```cron
-0 * * * * php /path/to/FreshRSS/app/actualize_script.php > /tmp/FreshRSS.log 2>&1
-```
-
-## Online cron
-
-If you do not have access to the installation server scheduled task, you can still automate the update process.
-
-To do so, you need to create a scheduled task, which need to call a specific URL:
-<https://freshrss.example.net/i/?c=feed&a=actualize> (it could be different depending on your installation). Depending on your application authentication method, you need to adapt the scheduled task.
-
-Special parameters to configure the script - all parameters can be combined:
-
-* Parameter "force"
-<https://freshrss.example.net/i/?c=feed&a=actualize&force=1>
-If *force* is set to 1 all feeds will be refreshed at once.
-
-* Parameter "ajax"
-<https://freshrss.example.net/i/?c=feed&a=actualize&ajax=1>
-Only a status site is returned and not a complete website. Example: "OK"
-
-* Parameter "maxFeeds"
-<https://freshrss.example.net/i/?c=feed&a=actualize&maxFeeds=30>
-If *maxFeeds* is set the configured amount of feeds is refreshed at once. The default setting is "10".
-
-* Parameter "token"
-<https://freshrss.example.net/i/?c=feed&a=actualize&token=542345872345734>
-Security parameter to prevent unauthorized refreshes. For detailed Documentation see "Form authentication".
-
-### For Form Authentication
-
-If your FreshRSS instance is using Form Authentication, you can configure an authentication token to grant access to the online cron.
-
-![Token configuration](../img/users/token.1.png)
-
-You can target a specific user by adding their username to the query string, with `&user=insert-username`:
-
-The scheduled task syntax should look as follows:
-
-<https://freshrss.example.net/i/?c=feed&a=actualize&maxFeeds=10&ajax=1&user=someone&token=my-token>
-
-Alternatively, but not recommended, if you configure the application to allow anonymous reading, you can also allow anonymous users to update feeds (“Allow anonymous refresh of the articles”), and that does not require a token.
-
-![Anonymous access configuration](../img/users/anonymous_access.1.png)
-
-### For HTTP authentication
-
-If your FreshRSS instance is using HTTP authentication, you’ll need to provide your credentials to the scheduled task.
-
-**Note:** This method is discouraged as your credentials are stored in plain text.
-
-```cron
-0 * * * * curl -u alice:password123 'https://freshrss.example.net/i/?c=feed&a=actualize&maxFeeds=10&ajax=1&user=alice'
-```
-
-On some systems, that syntax might also work:
-
-<https://alice:password123@freshrss.example.net/i/?c=feed&a=actualize&maxFeeds=10&ajax=1&user=alice>
-
-### For No authentication (None)
-
-If your FreshRSS instance uses no authentication (public instance, default user):
-
-<https://freshrss.example.net/i/?c=feed&a=actualize&maxFeeds=10&ajax=1>
-
-## Manual update
-
-If you can’t or don’t want to use the automatic method, you can update manually. There are two methods for updating all or some of the feeds.
-
-### Complete update
-
-This update occurs on all feeds. To trigger it, simply click on the update link in the navigation menu.
-
-![Navigation menu](../img/users/refresh.1.png)
-
-When the update starts, a progress bar appears and changes while feeds are processed.
-
-![Progress bar](../img/users/refresh.5.png)
-
-### Partial update
-
-This update occurs on the selected feed only. To trigger it, simply click on the update link in the feed menu.
-
-![Feed menu](../img/users/refresh.2.png)
-
-# Filtering articles
-
-## Purpose
-
-When the number of articles stored by FreshRSS inevitably grows larger, it’s important to use efficient filters to display only a subset of the articles. There are several methods that filter with different criteria. Usually those methods can be combined.
-
-## How-to filter
-
-### By category
-
-This is the easiest method. You only need to click on the category title in the side panel. There are two special categories at the top of the panel:
-
-* *Main feed* displays only articles from feeds marked as available in that category
-* *Favourites* displays only articles marked as favourites
-
-### By feed
-
-There are several methods to filter articles by feed:
-
-* by clicking the feed title in the side panel
-* by clicking the feed title in the article details
-* by filtering in the feed options from the side panel
-* by filtering in the feed configuration
-
-![Feed filter](../img/users/feed.filter.1.png)
-
-### By status
-
-Each article has two attributes that can be combined. The first attribute indicates whether or not the article has been read. The second attribute indicates if the article was marked as favorite or not.
-
-In version 0.7, attribute filters are available in the article display dropdown list. With this version, it’s not possible to combine filters. For instance, it’s not possible to display only read and favorite articles.
-
-![Attribute filters in 0.7](../img/users/status.filter.0.7.png)
-
-Starting with version 0.8, all attribute filters are visible as toggle icons. They can be combined. As any combination is possible, some have the same result. For instance, the result for all filters selected is the same as no filter selected.
-
-![Attribute filters in 0.8](../img/users/status.filter.0.8.png)
-
-By default, this filter displays only unread articles
-
-### By content
-
-It is possible to filter articles by their content by inputting a string in the search field.
-
-### With the search field
-
-You can use the search field to further refine results:
-
-* by feed ID: `f:123` or multiple feed IDs (*or*): `f:123,234,345`
-* by author: `author:name` or `author:'composed name'`
-* by title: `intitle:keyword` or `intitle:'composed keyword'`
-* by URL: `inurl:keyword` or `inurl:'composed keyword'`
-* by tag: `#tag` or `#tag+with+whitespace`
-* by free-text: `keyword` or `'composed keyword'`
-* by date of discovery, using the [ISO 8601 time interval format](http://en.wikipedia.org/wiki/ISO_8601#Time_intervals): `date:<date-interval>`
- * From a specific day, or month, or year:
- * `date:2014-03-30`
- * `date:2014-03` or `date:201403`
- * `date:2014`
- * From a specific time of a given day:
- * `date:2014-05-30T13`
- * `date:2014-05-30T13:30`
- * Between two given dates:
- * `date:2014-02/2014-04`
- * `date:2014-02--2014-04`
- * `date:2014-02/04`
- * `date:2014-02-03/05`
- * `date:2014-02-03T22:00/22:15`
- * `date:2014-02-03T22:00/15`
- * After a given date:
- * `date:2014-03/`
- * Before a given date:
- * `date:/2014-03`
- * For a specific duration after a given date:
- * `date:2014-03/P1W`
- * For a specific duration before a given date:
- * `date:P1W/2014-05-25T23:59:59`
- * For the past duration before now (the trailing slash is optional):
- * `date:P1Y/` or `date:P1Y` (past year)
- * `date:P2M/` (past two months)
- * `date:P3W/` (past three weeks)
- * `date:P4D/` (past four days)
- * `date:PT5H/` (past five hours)
- * `date:PT30M/` (past thirty minutes)
- * `date:PT90S/` (past ninety seconds)
- * `date:P1DT1H/` (past one day and one hour)
-* by date of publication, using the same format: `pubdate:<date-interval>`
-* by custom label ID `L:12` or multiple label IDs: `L:12,13,14` or with any label: `L:*`
-* by custom label name `label:label`, `label:"my label"` or any label name from a list (*or*): `labels:"my label,my other label"`
-* by several label names (*and*): `label:"my label" label:"my other label"`
-* by entry (article) ID: `e:1639310674957894` or multiple entry IDs (*or*): `e:1639310674957894,1639310674957893`
-* by user query (saved search) name: `search:myQuery`, `search:"My query"` or saved search ID: `S:3`
- * internally, those references are replaced by the corresponding user query in the search expression
-
-Be careful not to enter a space between the operator and the search value.
-
-Some operators can be used negatively, to exclude articles, with the same syntax as above, but prefixed by a `!` or `-`:
-`!f:234`, `-author:name`, `-intitle:keyword`, `-inurl:keyword`, `-#tag`, `!keyword`, `!date:2019`, `!date:P1W`, `!pubdate:P3d/`.
-
-It is also possible to combine keywords to create a more precise filter.
-For example, you can enter multiple instances of `f:`, `author:`, `intitle:`, `inurl:`, `#`, and free-text.
-
-Combining several search criteria implies a logical *and*, but the keyword ` OR `
-can be used to combine several search criteria with a logical *or* instead: `author:Dupont OR author:Dupond`
-
-You don’t have to do anything special to combine multiple negative operators. Writing `!intitle:'thing1' !intitle:'thing2'` implies AND, see above. For more pointers on how AND and OR interact with negation, see [this GitHub comment](https://github.com/FreshRSS/FreshRSS/issues/3236#issuecomment-891219460).
-Additional reading: [De Morgan's laws](https://en.wikipedia.org/wiki/De_Morgan%27s_laws).
-
-Finally, parentheses may be used to express more complex queries, with basic negation support:
-
-* `(author:Alice OR intitle:hello) (author:Bob OR intitle:world)`
-* `(author:Alice intitle:hello) OR (author:Bob intitle:world)`
-* `!((author:Alice intitle:hello) OR (author:Bob intitle:world))`
-* `(author:Alice intitle:hello) !(author:Bob intitle:world)`
-* `!(S:1 OR S:2)`
-
-### By sorting by date
-
-You can change the sort order by clicking the toggle button available in the header.
-
-## Store your filters
-
-Once you came up with your perfect filter, it would be a shame if you need to recreate it every time you need to use it.
-
-Hopefully, there is a way to bookmark them for later use.
-We call them *user queries*.
-You can create as many as you want, the only limit is how they will be displayed on your screen.
-
-### Bookmark the current query
-
-Display the user queries drop-down by clicking the button next to the state buttons.
-![User queries drop-down](../img/users/user.queries.drop-down.empty.png)
-
-Then click on the bookmark action.
-
-Congratulations, you’re done!
-
-### Using a bookmarked query
-
-Display the user queries drop-down by clicking the button next to the state buttons.
-![User queries drop-down](../img/users/user.queries.drop-down.not.empty.png)
-
-Then click on the bookmarked query, the previously stored query will be applied.
-
-> Note that only the query is stored, not the articles.
-> The results you are seeing now could be different from the results on the day you've created the query.
+---
+Read more:
+* [Refreshing the feeds](./09_refreshing_feeds.md)
+* [Filter the feeds and search](./10_filter.md)
diff --git a/docs/en/users/04_Subscriptions.md b/docs/en/users/04_Subscriptions.md
index d50d92bf2..7b0a488f0 100644
--- a/docs/en/users/04_Subscriptions.md
+++ b/docs/en/users/04_Subscriptions.md
@@ -5,7 +5,7 @@
3. Paste the URL in the “Feed URL” field.
4. (optional): You can select the category for your feed. By default, it will be in “Uncategorized”.
5. (optional): If the subscription requires credentials, you can enter them in the "HTTP username" and "HTTP password" fields.
-6. (optional): You can set a timeout for the feed request if the feed requires it.
+6. (optional): You can set a timeout for the feed request.
7. (optional): You can choose to ignore SSL certificate errors (such as with self-signed certificates) by setting "Verify SSL security" to "No". This is not recommended, and it is better to either add the root certificate to the FreshRSS server or to fix the SSL certificate problems on the feed hosting server.
## Subscription management
@@ -89,7 +89,7 @@ Complementary tools can be used to retrieve full article content, such as:
### Filter
-Articles can be automatically marked as read based on some search terms. See [filtering](./03_Main_view.md#filtering-articles) for more information on how to create these filters.
+Articles can be automatically marked as read based on some search terms. See [filtering](./10_filter.md) for more information on how to create these filters.
## Import / export
diff --git a/docs/en/users/05_Configuration.md b/docs/en/users/05_Configuration.md
index 9ad6a58ca..1ce0e0a0d 100644
--- a/docs/en/users/05_Configuration.md
+++ b/docs/en/users/05_Configuration.md
@@ -13,16 +13,23 @@ Available languages are: cz, de, en, es, fr, he, it, ko, nl, oc, pt-br, ru, tr,
## Theme
-There’s no accounting for tastes, which is why FreshRSS offers eight official themes:
-
-* *Blue Lagoon* by **Mister aiR**
-* *Dark* by **AD**
-* *Flat design* by **Marien Fressinaud**
-* *Origine* by **Marien Fressinaud**
-* *Origine-compact* by **Kevin Papst**
-* *Pafat* by **Plopoyop**
-* *Screwdriver* by **Mister aiR**
-* *Swage* by **Patrick Crandol**
+There’s no accounting for tastes, which is why FreshRSS offers 13 official themes:
+
+| Theme | designed by | Notes |
+|:--------------|:-------------------------------------------------------|:--------------------------------------------------------------|
+| Alternative Dark | Ghost | |
+| Ansum | Thomas Guesnon | |
+| Blue Lagoon |Mister aiR | No longer supported. Will be removed with FreshRSS V1.22.0 |
+| Dark | AD | |
+| Dark pink | Miicat_47 | |
+| Flat design | Marien Fressinaud | No longer supported. Will be removed with FreshRSS V1.22.0 |
+| Mapco | Thomas Guesnon | |
+| Nord theme | joelchrono12 | |
+| Origine | Marien Fressinaud | (default theme) |
+| Origine-compact | Kevin Papst | |
+| Pafat | Plopoyop | |
+| Screwdriver | Mister aiR | No longer supported. Will be removed with FreshRSS V1.22.0 |
+| Swage | Patrick Crandol | |
If you can’t find any themes you like, it’s always possible to [create your own](../developers/04_Frontend/02_Design.md).
diff --git a/docs/en/users/09_refreshing_feeds.md b/docs/en/users/09_refreshing_feeds.md
new file mode 100644
index 000000000..6d9c2af55
--- /dev/null
+++ b/docs/en/users/09_refreshing_feeds.md
@@ -0,0 +1,134 @@
+# Refreshing feeds
+
+To take full advantage of FreshRSS, it needs to retrieve new items from the feeds you have subscribed to. There are several ways to do this:
+
+- [Manual update](#manual-update)
+ - [Complete update](#complete-update)
+ - [Partial update](#partial-update)
+- [Automatic update with cron](#automatic-update-with-cron)
+- [Online cron](#online-cron)
+ - [For Form Authentication](#for-form-authentication)
+ - [For HTTP authentication](#for-http-authentication)
+ - [For No authentication None](#for-no-authentication-none)
+- [Feed configuration of “Do not automatically refresh more often than”](#feed-configuration-of-do-not-automatically-refresh-more-often-than)
+ - [Background](#background)
+ - [Default value](#default-value)
+ - [Individual feed configuration](#individual-feed-configuration)
+
+## Manual update
+
+If you can’t or don’t want to use the automatic method, you can update manually. There are two methods for updating all or some of the feeds.
+
+### Complete update
+
+This update occurs on all feeds. To trigger it, simply click on the update link in the navigation menu.
+
+![Navigation menu](../img/users/refresh.1.png)
+
+When the update starts, a progress bar appears and changes while feeds are processed.
+
+![Progress bar](../img/users/refresh.5.png)
+
+### Partial update
+
+This update occurs on the selected feed only. To trigger it, simply click on the update link in the feed menu.
+
+![Feed menu](../img/users/refresh.2.png)
+
+## Automatic update with cron
+
+This is the recommended method.
+
+This method is only available if you have access to the scheduled tasks of the machine on which your FreshRSS instance is installed.
+
+The script is named *actualize_script.php* and is located in the *app* folder. The scheduled task syntax will not be explained here. However, here is [a quick introduction to crontab](http://www.adminschoice.com/crontab-quick-reference/) that might help you.
+
+Here is an example to trigger article update every hour.
+
+```cron
+0 * * * * php /path/to/FreshRSS/app/actualize_script.php > /tmp/FreshRSS.log 2>&1
+```
+
+## Online cron
+
+If you do not have access to the installation server scheduled task, you can still automate the update process.
+
+To do so, you need to create a scheduled task, which need to call a specific URL:
+<https://freshrss.example.net/i/?c=feed&a=actualize> (it could be different depending on your installation). Depending on your application authentication method, you need to adapt the scheduled task.
+
+Special parameters to configure the script - all parameters can be combined:
+
+- Parameter "force"
+<https://freshrss.example.net/i/?c=feed&a=actualize&force=1>
+If *force* is set to 1 all feeds will be refreshed at once.
+
+- Parameter "ajax"
+<https://freshrss.example.net/i/?c=feed&a=actualize&ajax=1>
+Only a status site is returned and not a complete website. Example: "OK"
+
+- Parameter "maxFeeds"
+<https://freshrss.example.net/i/?c=feed&a=actualize&maxFeeds=30>
+If *maxFeeds* is set the configured amount of feeds is refreshed at once. The default setting is "10".
+
+- Parameter "token"
+<https://freshrss.example.net/i/?c=feed&a=actualize&token=542345872345734>
+Security parameter to prevent unauthorized refreshes. For detailed Documentation see "Form authentication".
+
+### For Form Authentication
+
+If your FreshRSS instance is using Form Authentication, you can configure an authentication token to grant access to the online cron.
+
+![Token configuration](../img/users/token.1.png)
+
+You can target a specific user by adding their username to the query string, with `&user=insert-username`:
+
+The scheduled task syntax should look as follows:
+
+<https://freshrss.example.net/i/?c=feed&a=actualize&maxFeeds=10&ajax=1&user=someone&token=my-token>
+
+Alternatively, but not recommended, if you configure the application to allow anonymous reading, you can also allow anonymous users to update feeds (“Allow anonymous refresh of the articles”), and that does not require a token.
+
+![Anonymous access configuration](../img/users/anonymous_access.1.png)
+
+### For HTTP authentication
+
+If your FreshRSS instance is using HTTP authentication, you’ll need to provide your credentials to the scheduled task.
+
+**Note:** This method is discouraged as your credentials are stored in plain text.
+
+```cron
+0 * * * * curl -u alice:password123 'https://freshrss.example.net/i/?c=feed&a=actualize&maxFeeds=10&ajax=1&user=alice'
+```
+
+On some systems, that syntax might also work:
+
+<https://alice:password123@freshrss.example.net/i/?c=feed&a=actualize&maxFeeds=10&ajax=1&user=alice>
+
+### For No authentication (None)
+
+If your FreshRSS instance uses no authentication (public instance, default user):
+
+<https://freshrss.example.net/i/?c=feed&a=actualize&maxFeeds=10&ajax=1>
+
+## Feed configuration of “Do not automatically refresh more often than”
+
+### Background
+
+FreshRSS does not, by design, supports pull refreshes at frequencies higher than once every 15 minutes. But FreshRSS supports instant push (WebSub).
+
+FreshRSS is part of an RSS ecosystem. A typical reaction that we have seen from several servers is to simply ban by, IP, user-agent, or to remove their RSS feed altogether. Bad user behaviours affect the larger community.
+
+### Default value
+
+The default value of “Do not automatically refresh more often than” is set in Configuration -> Archiving.
+
+The lowest global/default purposely cannot be set faster than every 20 minutes, to avoid wasting resources and make sure the RSS ecosystem remains sane.
+
+### Individual feed configuration
+
+Under the settings for individual feeds, you can go down to 15min.
+
+---
+Read more:
+- [Normal, Global and Reader view](./03_Main_view.md)
+- [Filter the feeds and search](./10_filter.md)
diff --git a/docs/en/users/10_filter.md b/docs/en/users/10_filter.md
new file mode 100644
index 000000000..2e69b97f8
--- /dev/null
+++ b/docs/en/users/10_filter.md
@@ -0,0 +1,152 @@
+
+# Filtering articles
+
+## Purpose
+
+When the number of articles stored by FreshRSS inevitably grows larger, it’s important to use efficient filters to display only a subset of the articles. There are several methods that filter with different criteria. Usually those methods can be combined.
+
+## By category
+
+This is the easiest method. You only need to click on the category title in the side panel. There are two special categories at the top of the panel:
+
+* *Main feed* displays only articles from feeds marked as available in that category
+* *Favourites* displays only articles marked as favourites
+
+## By feed
+
+There are several methods to filter articles by feed:
+
+* by clicking the feed title in the side panel
+* by clicking the feed title in the article details
+* by filtering in the feed options from the side panel
+* by filtering in the feed configuration
+
+![Feed filter](../img/users/feed.filter.1.png)
+
+## By status
+
+Each article has two attributes that can be combined. The first attribute indicates whether or not the article has been read. The second attribute indicates if the article was marked as favorite or not.
+
+In version 0.7, attribute filters are available in the article display dropdown list. With this version, it’s not possible to combine filters. For instance, it’s not possible to display only read and favorite articles.
+
+![Attribute filters in 0.7](../img/users/status.filter.0.7.png)
+
+Starting with version 0.8, all attribute filters are visible as toggle icons. They can be combined. As any combination is possible, some have the same result. For instance, the result for all filters selected is the same as no filter selected.
+
+![Attribute filters in 0.8](../img/users/status.filter.0.8.png)
+
+By default, this filter displays only unread articles
+
+## By content
+
+It is possible to filter articles by their content by inputting a string in the search field.
+
+## With the search field
+
+You can use the search field to further refine results:
+
+* by feed ID: `f:123` or multiple feed IDs (*or*): `f:123,234,345`
+* by author: `author:name` or `author:'composed name'`
+* by title: `intitle:keyword` or `intitle:'composed keyword'`
+* by URL: `inurl:keyword` or `inurl:'composed keyword'`
+* by tag: `#tag` or `#tag+with+whitespace`
+* by free-text: `keyword` or `'composed keyword'`
+* by date of discovery, using the [ISO 8601 time interval format](http://en.wikipedia.org/wiki/ISO_8601#Time_intervals): `date:<date-interval>`
+ * From a specific day, or month, or year:
+ * `date:2014-03-30`
+ * `date:2014-03` or `date:201403`
+ * `date:2014`
+ * From a specific time of a given day:
+ * `date:2014-05-30T13`
+ * `date:2014-05-30T13:30`
+ * Between two given dates:
+ * `date:2014-02/2014-04`
+ * `date:2014-02--2014-04`
+ * `date:2014-02/04`
+ * `date:2014-02-03/05`
+ * `date:2014-02-03T22:00/22:15`
+ * `date:2014-02-03T22:00/15`
+ * After a given date:
+ * `date:2014-03/`
+ * Before a given date:
+ * `date:/2014-03`
+ * For a specific duration after a given date:
+ * `date:2014-03/P1W`
+ * For a specific duration before a given date:
+ * `date:P1W/2014-05-25T23:59:59`
+ * For the past duration before now (the trailing slash is optional):
+ * `date:P1Y/` or `date:P1Y` (past year)
+ * `date:P2M/` (past two months)
+ * `date:P3W/` (past three weeks)
+ * `date:P4D/` (past four days)
+ * `date:PT5H/` (past five hours)
+ * `date:PT30M/` (past thirty minutes)
+ * `date:PT90S/` (past ninety seconds)
+ * `date:P1DT1H/` (past one day and one hour)
+* by date of publication, using the same format: `pubdate:<date-interval>`
+* by custom label ID `L:12` or multiple label IDs: `L:12,13,14` or with any label: `L:*`
+* by custom label name `label:label`, `label:"my label"` or any label name from a list (*or*): `labels:"my label,my other label"`
+* by several label names (*and*): `label:"my label" label:"my other label"`
+* by entry (article) ID: `e:1639310674957894` or multiple entry IDs (*or*): `e:1639310674957894,1639310674957893`
+* by user query (saved search) name: `search:myQuery`, `search:"My query"` or saved search ID: `S:3`
+ * internally, those references are replaced by the corresponding user query in the search expression
+
+Be careful not to enter a space between the operator and the search value.
+
+Some operators can be used negatively, to exclude articles, with the same syntax as above, but prefixed by a `!` or `-`:
+`!f:234`, `-author:name`, `-intitle:keyword`, `-inurl:keyword`, `-#tag`, `!keyword`, `!date:2019`, `!date:P1W`, `!pubdate:P3d/`.
+
+It is also possible to combine keywords to create a more precise filter.
+For example, you can enter multiple instances of `f:`, `author:`, `intitle:`, `inurl:`, `#`, and free-text.
+
+Combining several search criteria implies a logical *and*, but the keyword ` OR `
+can be used to combine several search criteria with a logical *or* instead: `author:Dupont OR author:Dupond`
+
+You don’t have to do anything special to combine multiple negative operators. Writing `!intitle:'thing1' !intitle:'thing2'` implies AND, see above. For more pointers on how AND and OR interact with negation, see [this GitHub comment](https://github.com/FreshRSS/FreshRSS/issues/3236#issuecomment-891219460).
+Additional reading: [De Morgan's laws](https://en.wikipedia.org/wiki/De_Morgan%27s_laws).
+
+Finally, parentheses may be used to express more complex queries, with basic negation support:
+
+* `(author:Alice OR intitle:hello) (author:Bob OR intitle:world)`
+* `(author:Alice intitle:hello) OR (author:Bob intitle:world)`
+* `!((author:Alice intitle:hello) OR (author:Bob intitle:world))`
+* `(author:Alice intitle:hello) !(author:Bob intitle:world)`
+* `!(S:1 OR S:2)`
+
+> ℹ️ If you need to search for a parenthesis, it needs to be escaped like `\(` or `\)`
+
+## By sorting by date
+
+You can change the sort order by clicking the toggle button available in the header.
+
+## Store your filters
+
+Once you came up with your perfect filter, it would be a shame if you need to recreate it every time you need to use it.
+
+Hopefully, there is a way to bookmark them for later use.
+We call them *user queries*.
+You can create as many as you want, the only limit is how they will be displayed on your screen.
+
+### Bookmark the current query
+
+Display the user queries drop-down by clicking the button next to the state buttons.
+![User queries drop-down](../img/users/user.queries.drop-down.empty.png)
+
+Then click on the bookmark action.
+
+Congratulations, you’re done!
+
+### Using a bookmarked query
+
+Display the user queries drop-down by clicking the button next to the state buttons.
+![User queries drop-down](../img/users/user.queries.drop-down.not.empty.png)
+
+Then click on the bookmarked query, the previously stored query will be applied.
+
+> Note that only the query is stored, not the articles.
+> The results you are seeing now could be different from the results on the day you've created the query.
+
+---
+Read more:
+* [Normal, Global and Reader view](./03_Main_view.md)
+* [Refreshing the feeds](./09_refreshing_feeds.md)
diff --git a/docs/fr/developers/01_First_steps.md b/docs/fr/developers/01_First_steps.md
index 6c02b5058..f53cbaeb8 100644
--- a/docs/fr/developers/01_First_steps.md
+++ b/docs/fr/developers/01_First_steps.md
@@ -57,7 +57,7 @@ Vous pouvez arrêter les conteneurs en tapant <kbd>Control</kbd> + <kbd>c</kbd>
make stop
```
-Si la configuration vous intéresse, les commandes `make' sont définies dans
+Si la configuration vous intéresse, les commandes `make` sont définies dans
le fichier [`Makefile`](/Makefile).
Si vous avez besoin d’utiliser une image Docker identifiée par un tag
diff --git a/docs/fr/developers/03_Backend/02_Minz.md b/docs/fr/developers/03_Backend/02_Minz.md
deleted file mode 100644
index 5daf684f0..000000000
--- a/docs/fr/developers/03_Backend/02_Minz.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# Minz
-
-## Modèles
-
-> **À FAIRE**
-
-## Contrôleurs et actions
-
-> **À FAIRE**
-
-## Vues
-
-> **À FAIRE**
-
-## Routage
-
-> **À FAIRE**
-
-## Écriture des URL
-
-> **À FAIRE**
-
-## Internationalisation
-
-> **À FAIRE**
-
-## Comprendres les mécanismes internes
-
-> **À FAIRE**
diff --git a/docs/fr/developers/03_Backend/05_Extensions.md b/docs/fr/developers/03_Backend/05_Extensions.md
index a715c40b3..548aebf4f 100644
--- a/docs/fr/developers/03_Backend/05_Extensions.md
+++ b/docs/fr/developers/03_Backend/05_Extensions.md
@@ -36,329 +36,9 @@ puissent par la suite être intégrées dans le code initial de FreshRSS de
façon officielle. Cela permet de proposer un « proof of concept » assez
facilement.
-## Comprendre les mécaniques de base (Minz et MVC)
-
-**TODO** : bouger dans 02_Minz.md
-
-Cette fiche technique devrait renvoyer vers la documentation officielle de
-FreshRSS et de Minz (le framework PHP sur lequel repose
-FreshRSS). Malheureusement cette documentation n’existe pas encore. Voici
-donc en quelques mots les principaux éléments à connaître. Il n’est pas
-nécessaire de lire l’ensemble des chapitres de cette section si vous n’avez
-pas à utiliser une fonctionnalité dans votre extension (si vous n’avez pas
-besoin de traduire votre extension, pas besoin d’en savoir plus sur le
-module `Minz_Translate` par exemple).
-
-### Architecture MVC
-
-Minz repose et impose une architecture MVC pour les projets l’utilisant. On
-distingue dans cette architecture trois composants principaux :
-
-* Le Modèle : c’est l’objet de base que l’on va manipuler. Dans FreshRSS,
- les catégories, les flux et les articles sont des modèles. La partie du
- code qui permet de les manipuler en base de données fait aussi partie du
- modèle mais est séparée du modèle de base : on parle de DAO (pour « Data
- Access Object »). Les modèles sont stockés dans un répertoire `Models`.
-* La Vue : c’est ce qui représente ce que verra l’utilisateur. La vue est
- donc simplement du code HTML que l’on mixe avec du PHP pour afficher les
- informations dynamiques. Les vues sont stockées dans un répertoire
- `views`.
-* Le Contrôleur : c’est ce qui permet de lier modèles et vues entre
- eux. Typiquement, un contrôleur va charger des modèles à partir de la base
- de données (une liste d’articles par exemple) pour les « passer » à une
- vue afin qu’elle les affiche. Les contrôleurs sont stockés dans un
- répertoire `Controllers`.
-
-### Routage
-
-Afin de lier une URL à un contrôleur, on doit passer par une phase dite de «
-routage ». Dans FreshRSS, cela est particulièrement simple car il suffit
-d’indiquer le nom du contrôleur à charger dans l’URL à l’aide d’un paramètre `c`.
-Par exemple, l’adresse <http://exemple.com?c=hello> va exécuter le code
-contenu dans le contrôleur `hello`.
-
-Une notion qui n’a pas encore été évoquée est le système d'« actions ». Une
-action est exécutée *sur* un contrôleur. Concrètement, un contrôleur va être
-représenté par une classe et ses actions par des méthodes. Pour exécuter une
-action, il est nécessaire d’indiquer un paramètre `a` dans l’URL.
-
-Exemple de code :
-
-```php
-<?php
-
-class FreshRSS_hello_Controller extends FreshRSS_ActionController {
- public function indexAction() {
- $this->view->a_variable = 'FooBar';
- }
-
- public function worldAction() {
- $this->view->a_variable = 'Hello World!';
- }
-}
-
-?>
-```
-
-Si l’on charge l’adresse <http://exemple.com?c=hello&a=world>, l’action
-`world` va donc être exécutée sur le contrôleur `hello`.
-
-Note : si `c` ou `a` n’est pas précisée, la valeur par défaut de chacune de
-ces variables est `index`. Ainsi l’adresse <http://exemple.com?c=hello> va
-exécuter l’action `index` du contrôleur `hello`.
-
-Plus loin, sera utilisée la convention `hello/world` pour évoquer un couple
-contrôleur/action.
-
-### Vues
-
-Chaque vue est associée à un contrôleur et à une action. La vue associée à
-`hello/world` va être stockée dans un fichier bien spécifique :
-`views/hello/world.phtml`. Cette convention est imposée par Minz.
-
-Comme expliqué plus haut, les vues sont du code HTML mixé à du PHP. Exemple
-de code :
-
-```html
-<p>
- Phrase passée en paramètre : <?= $this->a_variable ?>
-</p>
-```
-
-La variable `$this->a_variable` a été passée précédemment par le contrôleur (voir exemple précédent). La différence est que dans le contrôleur il est nécessaire de passer par `$this->view` et que dans la vue `$this` suffit.
-
-### Accéder aux paramètres GET / POST
-
-Il est souvent nécessaire de profiter des paramètres passés par GET ou par
-POST. Dans Minz, ces paramètres sont accessibles de façon indistincts à
-l’aide de la classe `Minz_Request`. Exemple de code :
-
-```php
-<?php
-
-$default_value = 'foo';
-$param = Minz_Request::param('bar', $default_value);
-
-// Affichera la valeur du paramètre `bar` (passé via GET ou POST)
-// ou "foo" si le paramètre n’existe pas.
-echo $param;
-
-// Force la valeur du paramètre `bar`
-Minz_Request::_param('bar', 'baz');
-
-// Affichera forcément "baz" puisque nous venons de forcer sa valeur.
-// Notez que le second paramètre (valeur par défaut) est facultatif.
-echo Minz_Request::param('bar');
-
-?>
-```
-
-La méthode `Minz_Request::isPost()` peut être utile pour n’exécuter un
-morceau de code que s’il s’agit d’une requête POST.
-
-Note : il est préférable de n’utiliser `Minz_Request` que dans les
-contrôleurs. Il est probable que vous rencontriez cette méthode dans les
-vues de FreshRSS, voire dans les modèles, mais sachez qu’il ne s’agit
-**pas** d’une bonne pratique.
-
-### Accéder aux paramètres de session
-
-L’accès aux paramètres de session est étrangement similaire aux paramètres
-GET / POST mais passe par la classe `Minz_Session` cette fois-ci ! Il n’y a
-pas d’exemple ici car vous pouvez reprendre le précédent en changeant tous
-les `Minz_Request` par des `Minz_Session`.
-
-### Gestion des URL
-
-Pour profiter pleinement du système de routage de Minz, il est fortement
-déconseillé d’écrire les URL en dur dans votre code. Par exemple, la vue
-suivante doit être évitée :
-
-```html
-<p>
- Accéder à la page <a href="http://exemple.com?c=hello&amp;a=world">Hello world</a>!
-</p>
-```
-
-Si un jour il est décidé d’utiliser un système d'« url rewriting » pour
-avoir des adresses au format <http://exemple.com/controller/action>, toutes
-les adresses précédentes deviendraient ineffectives !
-
-Préférez donc l’utilisation de la classe `Minz_Url` et de sa méthode
-`display()`. `Minz_Url::display()` prend en paramètre un tableau de la forme
-suivante :
-
-```php
-<?php
-
-$url_array = [
- 'c' => 'hello',
- 'a' => 'world',
- 'params' => [
- 'foo' => 'bar',
- ],
-];
-
-// Affichera quelque chose comme .?c=hello&amp;a=world&amp;foo=bar
-echo Minz_Url::display($url_array);
-
-?>
-```
-
-Comme cela peut devenir un peu pénible à utiliser à la longue, surtout dans
-les vues, il est préférable d’utiliser le raccourci `_url()` :
-
-```php
-<?php
-
-// Affichera la même chose que précédemment
-echo _url('hello', 'world', 'foo', 'bar');
-
-?>
-```
-
-Note : en règle générale, la forme raccourcie (`_url()`) doit être utilisée
-dans les vues tandis que la forme longue (`Minz_Url::display()`) doit être
-utilisée dans les contrôleurs.
-
-### Redirections
-
-Il est souvent nécessaire de rediriger un utilisateur vers une autre
-page. Pour cela, la classe `Minz_Request` dispose d’une autre méthode utile
-: `forward()`. Cette méthode prend en argument le même format d’URL que
-celui vu juste avant.
-
-Exemple de code :
-
-```php
-<?php
-
-$url_array = [
- 'c' => 'hello',
- 'a' => 'world',
-];
-
-// Indique à Minz de rediriger l’utilisateur vers la page hello/world.
-// Notez qu’il s’agit d’une redirection au sens Minz du terme, pas d’une redirection que le navigateur va avoir à gérer (code HTTP 301 ou 302)
-// Le code qui suit forward() va ainsi être exécuté !
-Minz_Request::forward($url_array);
-
-// Pour effectuer une redirection type 302, ajoutez "true".
-// Le code qui suivra ne sera alors jamais exécuté.
-Minz_Request::forward($url_array, true);
-
-?>
-```
-
-Il est très fréquent de vouloir effectuer une redirection tout en affichant
-un message à l’utilisateur pour lui indiquer comment s’est déroulée l’action
-effectuée juste avant (validation d’un formulaire par exemple). Un tel
-message est passé par une variable de session `notification` (note : nous
-parlerons plutôt de « feedback » désormais pour éviter la confusion avec une
-notification qui peut survenir à tout moment). Pour faciliter ce genre
-d’action très fréquente, il existe deux raccourcis qui effectuent tout deux
-une redirection type 302 en affectant un message de feedback :
-
-```php
-<?php
-
-$url_array = [
- 'c' => 'hello',
- 'a' => 'world',
-];
-$feedback_good = 'Tout s’est bien passé !';
-$feedback_bad = 'Oups, quelque chose n’a pas marché.';
-
-Minz_Request::good($feedback_good, $url_array);
-
-// ou
-
-Minz_Request::bad($feedback_bad, $url_array);
-
-?>
-```
-
-### Gestion de la traduction
-
-Il est fréquent (et c’est un euphémisme) de vouloir afficher des phrases à
-l’utilisateur. Dans l’exemple précédent par exemple, nous affichions un
-feedback à l’utilisateur en fonction du résultat d’une validation de
-formulaire. Le problème est que FreshRSS possède des utilisateurs de
-différentes nationalités. Il est donc nécessaire de pouvoir gérer
-différentes langues pour ne pas rester cantonné à l’Anglais ou au Français.
-
-La solution consiste à utiliser la classe `Minz_Translate` qui permet de
-traduire dynamiquement FreshRSS (ou toute application basée sur Minz). Avant
-d’utiliser ce module, il est nécessaire de savoir où trouver les chaînes de
-caractères à traduire. Chaque langue possède son propre sous-répertoire dans
-un répertoire parent nommé `i18n`. Par exemple, les fichiers de langue en
-Français sont situés dans `i18n/fr/`. Il existe sept fichiers différents :
-
-* `admin.php` pour tout ce qui est relatif à l’administration de FreshRSS ;
-* `conf.php` pour l’aspect configuration ;
-* `feedback.php` contient les traductions des messages de feedback ;
-* `gen.php` stocke ce qui est global à FreshRSS (gen pour « general ») ;
-* `index.php` pour la page principale qui liste les flux et la page « À propos » ;
-* `install.php` contient les phrases relatives à l’installation de FreshRSS ;
-* `sub.php` pour l’aspect gestion des abonnements (sub pour « subscription »).
-
-Cette organisation permet de ne pas avoir un unique énorme fichier de
-traduction.
-
-Les fichiers de traduction sont assez simples : il s’agit seulement de
-retourner un tableau PHP contenant les traductions. Extrait du fichier
-`app/i18n/fr/gen.php` :
-
-```php
-<?php
-
-return array(
- 'action' => [
- 'actualize' => 'Actualiser',
- 'back_to_rss_feeds' => '← Retour à vos flux RSS',
- 'cancel' => 'Annuler',
- 'create' => 'Créer',
- 'disable' => 'Désactiver',
- ),
- 'freshrss' => array(
- '_' => 'FreshRSS',
- 'about' => 'À propos de FreshRSS',
- ),
-];
-
-?>
-```
-
-Pour accéder à ces traductions, `Minz_Translate` va nous aider à l’aide de
-sa méthode `Minz_Translate::t()`. Comme cela peut être un peu long à taper,
-il a été introduit un raccourci qui **doit** être utilisé en toutes
-circonstances : `_t()`. Exemple de code :
-
-```html
-<p>
- <a href="<?= _url('index', 'index') ?>">
- <?= _t('gen.action.back_to_rss_feeds') ?>
- </a>
-</p>
-```
+## Minz Framework
-La chaîne à passer à la fonction `_t()` consiste en une série d’identifiants
-séparés par des points. Le premier identifiant indique de quel fichier on
-veut extraire la traduction (dans notre cas présent, de `gen.php`), tandis
-que les suivantes indiquent des entrées de tableaux. Ainsi `action` est une
-entrée du tableau principal et `back_to_rss_feeds` est une entrée du tableau
-`action`. Cela permet d’organiser encore un peu plus nos fichiers de
-traduction.
-
-Il existe un petit cas particulier qui permet parfois de se simplifier la
-vie : le cas de l’identifiant `_`. Celui-ci doit nécessairement être présent
-en bout de chaîne et permet de donner une valeur à l’identifiant de niveau
-supérieur. C’est assez dur à expliquer mais très simple à comprendre. Dans
-l’exemple donné plus haut, un `_` est associé à la valeur `FreshRSS` : cela
-signifie qu’il n’y a pas besoin d’écrire `_t('gen.freshrss._')` mais
-`_t('gen.freshrss')` suffit.
-
-### Gestion de la configuration
+see [Minz documentation](/docs/fr/developers/Minz/index.md)
## Écrire une extension pour FreshRSS
diff --git a/docs/fr/developers/Minz/index.md b/docs/fr/developers/Minz/index.md
new file mode 100644
index 000000000..0d1d2124a
--- /dev/null
+++ b/docs/fr/developers/Minz/index.md
@@ -0,0 +1,249 @@
+# Minz
+
+Cette fiche technique devrait renvoyer vers la documentation officielle de
+FreshRSS et de Minz (le framework PHP sur lequel repose
+FreshRSS). Malheureusement cette documentation n’existe pas encore. Voici
+donc en quelques mots les principaux éléments à connaître. Il n’est pas
+nécessaire de lire l’ensemble des chapitres de cette section si vous n’avez
+pas à utiliser une fonctionnalité dans votre extension (si vous n’avez pas
+besoin de traduire votre extension, pas besoin d’en savoir plus sur le
+module `Minz_Translate` par exemple).
+
+## Architecture MVC
+
+Minz repose et impose une architecture MVC pour les projets l’utilisant. On
+distingue dans cette architecture trois composants principaux :
+
+* Le Modèle : c’est l’objet de base que l’on va manipuler. Dans FreshRSS,
+ les catégories, les flux et les articles sont des modèles. La partie du
+ code qui permet de les manipuler en base de données fait aussi partie du
+ modèle mais est séparée du modèle de base : on parle de DAO (pour « Data
+ Access Object »). Les modèles sont stockés dans un répertoire `Models`.
+* La Vue : c’est ce qui représente ce que verra l’utilisateur. La vue est
+ donc simplement du code HTML que l’on mixe avec du PHP pour afficher les
+ informations dynamiques. Les vues sont stockées dans un répertoire
+ `views`.
+* Le Contrôleur : c’est ce qui permet de lier modèles et vues entre
+ eux. Typiquement, un contrôleur va charger des modèles à partir de la base
+ de données (une liste d’articles par exemple) pour les « passer » à une
+ vue afin qu’elle les affiche. Les contrôleurs sont stockés dans un
+ répertoire `Controllers`.
+
+## Routage
+
+Afin de lier une URL à un contrôleur, on doit passer par une phase dite de «
+routage ». Dans FreshRSS, cela est particulièrement simple car il suffit
+d’indiquer le nom du contrôleur à charger dans l’URL à l’aide d’un paramètre `c`.
+Par exemple, l’adresse <http://exemple.com?c=hello> va exécuter le code
+contenu dans le contrôleur `hello`.
+
+Une notion qui n’a pas encore été évoquée est le système d'« actions ». Une
+action est exécutée *sur* un contrôleur. Concrètement, un contrôleur va être
+représenté par une classe et ses actions par des méthodes. Pour exécuter une
+action, il est nécessaire d’indiquer un paramètre `a` dans l’URL.
+
+Exemple de code :
+
+```php
+<?php
+
+class FreshRSS_hello_Controller extends FreshRSS_ActionController {
+ public function indexAction() {
+ $this->view->a_variable = 'FooBar';
+ }
+
+ public function worldAction() {
+ $this->view->a_variable = 'Hello World!';
+ }
+}
+
+?>
+```
+
+Si l’on charge l’adresse <http://exemple.com?c=hello&a=world>, l’action
+`world` va donc être exécutée sur le contrôleur `hello`.
+
+Note : si `c` ou `a` n’est pas précisée, la valeur par défaut de chacune de
+ces variables est `index`. Ainsi l’adresse <http://exemple.com?c=hello> va
+exécuter l’action `index` du contrôleur `hello`.
+
+Plus loin, sera utilisée la convention `hello/world` pour évoquer un couple
+contrôleur/action.
+
+## Vues
+
+Chaque vue est associée à un contrôleur et à une action. La vue associée à
+`hello/world` va être stockée dans un fichier bien spécifique :
+`views/hello/world.phtml`. Cette convention est imposée par Minz.
+
+Comme expliqué plus haut, les vues sont du code HTML mixé à du PHP. Exemple
+de code :
+
+```html
+<p>
+ Phrase passée en paramètre : <?= $this->a_variable ?>
+</p>
+```
+
+La variable `$this->a_variable` a été passée précédemment par le contrôleur (voir exemple précédent). La différence est que dans le contrôleur il est nécessaire de passer par `$this->view` et que dans la vue `$this` suffit.
+
+## Accéder aux paramètres GET / POST
+
+Il est souvent nécessaire de profiter des paramètres passés par GET ou par
+POST. Dans Minz, ces paramètres sont accessibles de façon indistincts à
+l’aide de la classe `Minz_Request`. Exemple de code :
+
+```php
+<?php
+
+$default_value = 'foo';
+$param = Minz_Request::param('bar', $default_value);
+
+// Affichera la valeur du paramètre `bar` (passé via GET ou POST)
+// ou "foo" si le paramètre n’existe pas.
+echo $param;
+
+// Force la valeur du paramètre `bar`
+Minz_Request::_param('bar', 'baz');
+
+// Affichera forcément "baz" puisque nous venons de forcer sa valeur.
+// Notez que le second paramètre (valeur par défaut) est facultatif.
+echo Minz_Request::param('bar');
+
+?>
+```
+
+La méthode `Minz_Request::isPost()` peut être utile pour n’exécuter un
+morceau de code que s’il s’agit d’une requête POST.
+
+Note : il est préférable de n’utiliser `Minz_Request` que dans les
+contrôleurs. Il est probable que vous rencontriez cette méthode dans les
+vues de FreshRSS, voire dans les modèles, mais sachez qu’il ne s’agit
+**pas** d’une bonne pratique.
+
+## Accéder aux paramètres de session
+
+L’accès aux paramètres de session est étrangement similaire aux paramètres
+GET / POST mais passe par la classe `Minz_Session` cette fois-ci ! Il n’y a
+pas d’exemple ici car vous pouvez reprendre le précédent en changeant tous
+les `Minz_Request` par des `Minz_Session`.
+
+## Gestion des URL
+
+Pour profiter pleinement du système de routage de Minz, il est fortement
+déconseillé d’écrire les URL en dur dans votre code. Par exemple, la vue
+suivante doit être évitée :
+
+```html
+<p>
+ Accéder à la page <a href="http://exemple.com?c=hello&amp;a=world">Hello world</a>!
+</p>
+```
+
+Si un jour il est décidé d’utiliser un système d'« url rewriting » pour
+avoir des adresses au format <http://exemple.com/controller/action>, toutes
+les adresses précédentes deviendraient ineffectives !
+
+Préférez donc l’utilisation de la classe `Minz_Url` et de sa méthode
+`display()`. `Minz_Url::display()` prend en paramètre un tableau de la forme
+suivante :
+
+```php
+<?php
+
+$url_array = [
+ 'c' => 'hello',
+ 'a' => 'world',
+ 'params' => [
+ 'foo' => 'bar',
+ ],
+];
+
+// Affichera quelque chose comme .?c=hello&amp;a=world&amp;foo=bar
+echo Minz_Url::display($url_array);
+
+?>
+```
+
+Comme cela peut devenir un peu pénible à utiliser à la longue, surtout dans
+les vues, il est préférable d’utiliser le raccourci `_url()` :
+
+```php
+<?php
+
+// Affichera la même chose que précédemment
+echo _url('hello', 'world', 'foo', 'bar');
+
+?>
+```
+
+Note : en règle générale, la forme raccourcie (`_url()`) doit être utilisée
+dans les vues tandis que la forme longue (`Minz_Url::display()`) doit être
+utilisée dans les contrôleurs.
+
+## Redirections
+
+Il est souvent nécessaire de rediriger un utilisateur vers une autre
+page. Pour cela, la classe `Minz_Request` dispose d’une autre méthode utile
+: `forward()`. Cette méthode prend en argument le même format d’URL que
+celui vu juste avant.
+
+Exemple de code :
+
+```php
+<?php
+
+$url_array = [
+ 'c' => 'hello',
+ 'a' => 'world',
+];
+
+// Indique à Minz de rediriger l’utilisateur vers la page hello/world.
+// Notez qu’il s’agit d’une redirection au sens Minz du terme, pas d’une redirection que le navigateur va avoir à gérer (code HTTP 301 ou 302)
+// Le code qui suit forward() va ainsi être exécuté !
+Minz_Request::forward($url_array);
+
+// Pour effectuer une redirection type 302, ajoutez "true".
+// Le code qui suivra ne sera alors jamais exécuté.
+Minz_Request::forward($url_array, true);
+
+?>
+```
+
+Il est très fréquent de vouloir effectuer une redirection tout en affichant
+un message à l’utilisateur pour lui indiquer comment s’est déroulée l’action
+effectuée juste avant (validation d’un formulaire par exemple). Un tel
+message est passé par une variable de session `notification` (note : nous
+parlerons plutôt de « feedback » désormais pour éviter la confusion avec une
+notification qui peut survenir à tout moment). Pour faciliter ce genre
+d’action très fréquente, il existe deux raccourcis qui effectuent tout deux
+une redirection type 302 en affectant un message de feedback :
+
+```php
+<?php
+
+$url_array = [
+ 'c' => 'hello',
+ 'a' => 'world',
+];
+$feedback_good = 'Tout s’est bien passé !';
+$feedback_bad = 'Oups, quelque chose n’a pas marché.';
+
+Minz_Request::good($feedback_good, $url_array);
+
+// ou
+
+Minz_Request::bad($feedback_bad, $url_array);
+
+?>
+```
+
+## Gestion de la traduction
+
+Cette partie est [expliquée dans la page dédiée](/docs/fr/internationalization.md).
+
+## Migration
+
+Existing documentation includes:
+
+* [How to manage migrations](migrations.md)
diff --git a/docs/fr/developers/Minz/migration.md b/docs/fr/developers/Minz/migration.md
new file mode 100644
index 000000000..ad2eef949
--- /dev/null
+++ b/docs/fr/developers/Minz/migration.md
@@ -0,0 +1,3 @@
+# Migration
+
+see [English documentation](/docs/en/developers/Minz/migrations.md)
diff --git a/docs/fr/internationalization.md b/docs/fr/internationalization.md
new file mode 100644
index 000000000..532ed457d
--- /dev/null
+++ b/docs/fr/internationalization.md
@@ -0,0 +1,79 @@
+# Gestion de la traduction
+
+Il est fréquent (et c’est un euphémisme) de vouloir afficher des phrases à
+l’utilisateur. Dans l’exemple précédent par exemple, nous affichions un
+feedback à l’utilisateur en fonction du résultat d’une validation de
+formulaire. Le problème est que FreshRSS possède des utilisateurs de
+différentes nationalités. Il est donc nécessaire de pouvoir gérer
+différentes langues pour ne pas rester cantonné à l’Anglais ou au Français.
+
+La solution consiste à utiliser la classe `Minz_Translate` qui permet de
+traduire dynamiquement FreshRSS (ou toute application basée sur Minz). Avant
+d’utiliser ce module, il est nécessaire de savoir où trouver les chaînes de
+caractères à traduire. Chaque langue possède son propre sous-répertoire dans
+un répertoire parent nommé `i18n`. Par exemple, les fichiers de langue en
+Français sont situés dans `i18n/fr/`. Il existe sept fichiers différents :
+
+* `admin.php` pour tout ce qui est relatif à l’administration de FreshRSS ;
+* `conf.php` pour l’aspect configuration ;
+* `feedback.php` contient les traductions des messages de feedback ;
+* `gen.php` stocke ce qui est global à FreshRSS (gen pour « general ») ;
+* `index.php` pour la page principale qui liste les flux et la page « À propos » ;
+* `install.php` contient les phrases relatives à l’installation de FreshRSS ;
+* `sub.php` pour l’aspect gestion des abonnements (sub pour « subscription »).
+
+Cette organisation permet de ne pas avoir un unique énorme fichier de
+traduction.
+
+Les fichiers de traduction sont assez simples : il s’agit seulement de
+retourner un tableau PHP contenant les traductions. Extrait du fichier
+`app/i18n/fr/gen.php` :
+
+```php
+<?php
+
+return array(
+ 'action' => [
+ 'actualize' => 'Actualiser',
+ 'back_to_rss_feeds' => '← Retour à vos flux RSS',
+ 'cancel' => 'Annuler',
+ 'create' => 'Créer',
+ 'disable' => 'Désactiver',
+ ),
+ 'freshrss' => array(
+ '_' => 'FreshRSS',
+ 'about' => 'À propos de FreshRSS',
+ ),
+];
+
+?>
+```
+
+Pour accéder à ces traductions, `Minz_Translate` va nous aider à l’aide de
+sa méthode `Minz_Translate::t()`. Comme cela peut être un peu long à taper,
+il a été introduit un raccourci qui **doit** être utilisé en toutes
+circonstances : `_t()`. Exemple de code :
+
+```html
+<p>
+ <a href="<?= _url('index', 'index') ?>">
+ <?= _t('gen.action.back_to_rss_feeds') ?>
+ </a>
+</p>
+```
+
+La chaîne à passer à la fonction `_t()` consiste en une série d’identifiants
+séparés par des points. Le premier identifiant indique de quel fichier on
+veut extraire la traduction (dans notre cas présent, de `gen.php`), tandis
+que les suivantes indiquent des entrées de tableaux. Ainsi `action` est une
+entrée du tableau principal et `back_to_rss_feeds` est une entrée du tableau
+`action`. Cela permet d’organiser encore un peu plus nos fichiers de
+traduction.
+
+Il existe un petit cas particulier qui permet parfois de se simplifier la
+vie : le cas de l’identifiant `_`. Celui-ci doit nécessairement être présent
+en bout de chaîne et permet de donner une valeur à l’identifiant de niveau
+supérieur. C’est assez dur à expliquer mais très simple à comprendre. Dans
+l’exemple donné plus haut, un `_` est associé à la valeur `FreshRSS` : cela
+signifie qu’il n’y a pas besoin d’écrire `_t('gen.freshrss._')` mais
+`_t('gen.freshrss')` suffit.
diff --git a/docs/fr/users/01_Installation.md b/docs/fr/users/01_Installation.md
index 0a04206c2..221e0ff4e 100644
--- a/docs/fr/users/01_Installation.md
+++ b/docs/fr/users/01_Installation.md
@@ -7,7 +7,7 @@ Il est toutefois de votre responsabilité de vérifier que votre hébergement pe
| Logiciel | Recommandé | Fonctionne aussi avec |
| -------- | ----------- | --------------------- |
| Serveur web | **Apache 2** | Nginx |
-| PHP | **PHP 7+** | |
+| PHP | **PHP 7.2+** | |
| Modules PHP | Requis : libxml, cURL, JSON, PDO_MySQL, PCRE et ctype<br />Requis (32 bits seulement) : GMP<br />Recommandé : Zlib, mbstring et iconv, ZipArchive<br />*Pour une liste complète des modules nécessaires voir le [Dockerfile](https://github.com/FreshRSS/FreshRSS/blob/edge/Docker/Dockerfile-Alpine#L7-L9)* | |
| Base de données | **MySQL 5.5.3+** | SQLite 3.7.4+, PostgreSQL 9.5+ |
| Navigateur | **Firefox** | Chrome, Opera, Safari, or Edge |
diff --git a/docs/fr/users/03_Main_view.md b/docs/fr/users/03_Main_view.md
index 3a65c1f7f..3ca3b907c 100644
--- a/docs/fr/users/03_Main_view.md
+++ b/docs/fr/users/03_Main_view.md
@@ -275,3 +275,5 @@ Enfin, les parenthèses peuvent être utilisées pour des expressions plus compl
* `!((author:Alice intitle:bonjour) OR (author:Bob intitle:monde))`
* `(author:Alice intitle:bonjour) !(author:Bob intitle:monde)`
* `!(S:1 OR S:2)`
+
+> ℹ️ Si vous devez chercher une parenthèse, elle doit être *échappée* comme suit : `\(` ou `\)`
diff --git a/docs/fr/users/05_Configuration.md b/docs/fr/users/05_Configuration.md
index 88478a280..c64572d01 100644
--- a/docs/fr/users/05_Configuration.md
+++ b/docs/fr/users/05_Configuration.md
@@ -21,16 +21,23 @@ pt-br, ru, tr, zh-cn.
## Thème
Les goûts et les couleurs, ça ne se discute pas. C’est pourquoi FreshRSS
-propose huit thèmes officiels :
-
-* *Blue Lagoon* par **Mister aiR**
-* *Dark* par **AD**
-* *Flat design* par **Marien Fressinaud**
-* *Origine* par **Marien Fressinaud**
-* *Origine-compact* par **Kevin Papst**
-* *Pafat* par **Plopoyop**
-* *Screwdriver* par **Mister aiR**
-* *Swage* par **Patrick Crandol**
+propose 13 thèmes officiels :
+
+| Thème | Auteur | Notes |
+|:--------------|:-------------------------------------------------------|:--------------------------------------------------------------|
+| Alternative Dark | Ghost | |
+| Ansum | Thomas Guesnon | |
+| Blue Lagoon |Mister aiR | N'est plus pris en charge. Sera supprimé avec FreshRSS V1.22.0 |
+| Dark | AD | |
+| Dark pink | Miicat_47 | |
+| Flat design | Marien Fressinaud | N'est plus pris en charge. Sera supprimé avec FreshRSS V1.22.0 |
+| Mapco | Thomas Guesnon | |
+| Nord theme | joelchrono12 | |
+| Origine | Marien Fressinaud | (default theme) |
+| Origine-compact | Kevin Papst | |
+| Pafat | Plopoyop | |
+| Screwdriver | Mister aiR | N'est plus pris en charge. Sera supprimé avec FreshRSS V1.22.0 |
+| Swage | Patrick Crandol | |
Si aucun de ceux proposés ne convient, il est toujours possible de [créer
son propre thème](../developers/04_Frontend/02_Design.md).