/** * REST API: WP_REST_Request class * * @package WordPress * @subpackage REST_API * @since 4.4.0 */ /** * Core class used to implement a REST request object. * * Contains data from the request, to be passed to the callback. * * Note: This implements ArrayAccess, and acts as an array of parameters when * used in that manner. It does not use ArrayObject (as we cannot rely on SPL), * so be aware it may have non-array behaviour in some cases. * * Note: When using features provided by ArrayAccess, be aware that WordPress deliberately * does not distinguish between arguments of the same name for different request methods. * For instance, in a request with `GET id=1` and `POST id=2`, `$request['id']` will equal * 2 (`POST`) not 1 (`GET`). For more precision between request methods, use * WP_REST_Request::get_body_params(), WP_REST_Request::get_url_params(), etc. * * @since 4.4.0 * * @see ArrayAccess */ class WP_REST_Request implements ArrayAccess { /** * HTTP method. * * @since 4.4.0 * @var string */ protected $method = ''; /** * Parameters passed to the request. * * These typically come from the `$_GET`, `$_POST` and `$_FILES` * superglobals when being created from the global scope. * * @since 4.4.0 * @var array Contains GET, POST and FILES keys mapping to arrays of data. */ protected $params; /** * HTTP headers for the request. * * @since 4.4.0 * @var array Map of key to value. Key is always lowercase, as per HTTP specification. */ protected $headers = array(); /** * Body data. * * @since 4.4.0 * @var string Binary data from the request. */ protected $body = null; /** * Route matched for the request. * * @since 4.4.0 * @var string */ protected $route; /** * Attributes (options) for the route that was matched. * * This is the options array used when the route was registered, typically * containing the callback as well as the valid methods for the route. * * @since 4.4.0 * @var array Attributes for the request. */ protected $attributes = array(); /** * Used to determine if the JSON data has been parsed yet. * * Allows lazy-parsing of JSON data where possible. * * @since 4.4.0 * @var bool */ protected $parsed_json = false; /** * Used to determine if the body data has been parsed yet. * * @since 4.4.0 * @var bool */ protected $parsed_body = false; /** * Constructor. * * @since 4.4.0 * * @param string $method Optional. Request method. Default empty. * @param string $route Optional. Request route. Default empty. * @param array $attributes Optional. Request attributes. Default empty array. */ public function __construct( $method = '', $route = '', $attributes = array() ) { $this->params = array( 'URL' => array(), 'GET' => array(), 'POST' => array(), 'FILES' => array(), // See parse_json_params. 'JSON' => null, 'defaults' => array(), ); $this->set_method( $method ); $this->set_route( $route ); $this->set_attributes( $attributes ); } /** * Retrieves the HTTP method for the request. * * @since 4.4.0 * * @return string HTTP method. */ public function get_method() { return $this->method; } /** * Sets HTTP method for the request. * * @since 4.4.0 * * @param string $method HTTP method. */ public function set_method( $method ) { $this->method = strtoupper( $method ); } /** * Retrieves all headers from the request. * * @since 4.4.0 * * @return array Map of key to value. Key is always lowercase, as per HTTP specification. */ public function get_headers() { return $this->headers; } /** * Canonicalizes the header name. * * Ensures that header names are always treated the same regardless of * source. Header names are always case insensitive. * * Note that we treat `-` (dashes) and `_` (underscores) as the same * character, as per header parsing rules in both Apache and nginx. * * @link https://stackoverflow.com/q/18185366 * @link https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#missing-disappearing-http-headers * @link https://nginx.org/en/docs/http/ngx_http_core_module.html#underscores_in_headers * * @since 4.4.0 * @static * * @param string $key Header name. * @return string Canonicalized name. */ public static function canonicalize_header_name( $key ) { $key = strtolower( $key ); $key = str_replace( '-', '_', $key ); return $key; } /** * Retrieves the given header from the request. * * If the header has multiple values, they will be concatenated with a comma * as per the HTTP specification. Be aware that some non-compliant headers * (notably cookie headers) cannot be joined this way. * * @since 4.4.0 * * @param string $key Header name, will be canonicalized to lowercase. * @return string|null String value if set, null otherwise. */ public function get_header( $key ) { $key = $this->canonicalize_header_name( $key ); if ( ! isset( $this->headers[ $key ] ) ) { return null; } return implode( ',', $this->headers[ $key ] ); } /** * Retrieves header values from the request. * * @since 4.4.0 * * @param string $key Header name, will be canonicalized to lowercase. * @return array|null List of string values if set, null otherwise. */ public function get_header_as_array( $key ) { $key = $this->canonicalize_header_name( $key ); if ( ! isset( $this->headers[ $key ] ) ) { return null; } return $this->headers[ $key ]; } /** * Sets the header on request. * * @since 4.4.0 * * @param string $key Header name. * @param string $value Header value, or list of values. */ public function set_header( $key, $value ) { $key = $this->canonicalize_header_name( $key ); $value = (array) $value; $this->headers[ $key ] = $value; } /** * Appends a header value for the given header. * * @since 4.4.0 * * @param string $key Header name. * @param string $value Header value, or list of values. */ public function add_header( $key, $value ) { $key = $this->canonicalize_header_name( $key ); $value = (array) $value; if ( ! isset( $this->headers[ $key ] ) ) { $this->headers[ $key ] = array(); } $this->headers[ $key ] = array_merge( $this->headers[ $key ], $value ); } /** * Removes all values for a header. * * @since 4.4.0 * * @param string $key Header name. */ public function remove_header( $key ) { $key = $this->canonicalize_header_name( $key ); unset( $this->headers[ $key ] ); } /** * Sets headers on the request. * * @since 4.4.0 * * @param array $headers Map of header name to value. * @param bool $override If true, replace the request's headers. Otherwise, merge with existing. */ public function set_headers( $headers, $override = true ) { if ( true === $override ) { $this->headers = array(); } foreach ( $headers as $key => $value ) { $this->set_header( $key, $value ); } } /** * Retrieves the content-type of the request. * * @since 4.4.0 * * @return array Map containing 'value' and 'parameters' keys. */ public function get_content_type() { $value = $this->get_header( 'content-type' ); if ( empty( $value ) ) { return null; } $parameters = ''; if ( strpos( $value, ';' ) ) { list( $value, $parameters ) = explode( ';', $value, 2 ); } $value = strtolower( $value ); if ( strpos( $value, '/' ) === false ) { return null; } // Parse type and subtype out. list( $type, $subtype ) = explode( '/', $value, 2 ); $data = compact( 'value', 'type', 'subtype', 'parameters' ); $data = array_map( 'trim', $data ); return $data; } /** * Retrieves the parameter priority order. * * Used when checking parameters in get_param(). * * @since 4.4.0 * * @return array List of types to check, in order of priority. */ protected function get_parameter_order() { $order = array(); $content_type = $this->get_content_type(); if ( $content_type['value'] === 'application/json' ) { $order[] = 'JSON'; } $this->parse_json_params(); // Ensure we parse the body data. $body = $this->get_body(); if ( 'POST' !== $this->method && ! empty( $body ) ) { $this->parse_body_params(); } $accepts_body_data = array( 'POST', 'PUT', 'PATCH', 'DELETE' ); if ( in_array( $this->method, $accepts_body_data ) ) { $order[] = 'POST'; } $order[] = 'GET'; $order[] = 'URL'; $order[] = 'defaults'; /** * Filters the parameter order. * * The order affects which parameters are checked when using get_param() and family. * This acts similarly to PHP's `request_order` setting. * * @since 4.4.0 * * @param array $order { * An array of types to check, in order of priority. * * @param string $type The type to check. * } * @param WP_REST_Request $this The request object. */ return apply_filters( 'rest_request_parameter_order', $order, $this ); } /** * Retrieves a parameter from the request. * * @since 4.4.0 * * @param string $key Parameter name. * @return mixed|null Value if set, null otherwise. */ public function get_param( $key ) { $order = $this->get_parameter_order(); foreach ( $order as $type ) { // Determine if we have the parameter for this type. if ( isset( $this->params[ $type ][ $key ] ) ) { return $this->params[ $type ][ $key ]; } } return null; } /** * Sets a parameter on the request. * * @since 4.4.0 * * @param string $key Parameter name. * @param mixed $value Parameter value. */ public function set_param( $key, $value ) { $order = $this->get_parameter_order(); $this->params[ $order[0] ][ $key ] = $value; } /** * Retrieves merged parameters from the request. * * The equivalent of get_param(), but returns all parameters for the request. * Handles merging all the available values into a single array. * * @since 4.4.0 * * @return array Map of key to value. */ public function get_params() { $order = $this->get_parameter_order(); $order = array_reverse( $order, true ); $params = array(); foreach ( $order as $type ) { // array_merge / the "+" operator will mess up // numeric keys, so instead do a manual foreach. foreach ( (array) $this->params[ $type ] as $key => $value ) { $params[ $key ] = $value; } } return $params; } /** * Retrieves parameters from the route itself. * * These are parsed from the URL using the regex. * * @since 4.4.0 * * @return array Parameter map of key to value. */ public function get_url_params() { return $this->params['URL']; } /** * Sets parameters from the route. * * Typically, this is set after parsing the URL. * * @since 4.4.0 * * @param array $params Parameter map of key to value. */ public function set_url_params( $params ) { $this->params['URL'] = $params; } /** * Retrieves parameters from the query string. * * These are the parameters you'd typically find in `$_GET`. * * @since 4.4.0 * * @return array Parameter map of key to value */ public function get_query_params() { return $this->params['GET']; } /** * Sets parameters from the query string. * * Typically, this is set from `$_GET`. * * @since 4.4.0 * * @param array $params Parameter map of key to value. */ public function set_query_params( $params ) { $this->params['GET'] = $params; } /** * Retrieves parameters from the body. * * These are the parameters you'd typically find in `$_POST`. * * @since 4.4.0 * * @return array Parameter map of key to value. */ public function get_body_params() { return $this->params['POST']; } /** * Sets parameters from the body. * * Typically, this is set from `$_POST`. * * @since 4.4.0 * * @param array $params Parameter map of key to value. */ public function set_body_params( $params ) { $this->params['POST'] = $params; } /** * Retrieves multipart file parameters from the body. * * These are the parameters you'd typically find in `$_FILES`. * * @since 4.4.0 * * @return array Parameter map of key to value */ public function get_file_params() { return $this->params['FILES']; } /** * Sets multipart file parameters from the body. * * Typically, this is set from `$_FILES`. * * @since 4.4.0 * * @param array $params Parameter map of key to value. */ public function set_file_params( $params ) { $this->params['FILES'] = $params; } /** * Retrieves the default parameters. * * These are the parameters set in the route registration. * * @since 4.4.0 * * @return array Parameter map of key to value */ public function get_default_params() { return $this->params['defaults']; } /** * Sets default parameters. * * These are the parameters set in the route registration. * * @since 4.4.0 * * @param array $params Parameter map of key to value. */ public function set_default_params( $params ) { $this->params['defaults'] = $params; } /** * Retrieves the request body content. * * @since 4.4.0 * * @return string Binary data from the request body. */ public function get_body() { return $this->body; } /** * Sets body content. * * @since 4.4.0 * * @param string $data Binary data from the request body. */ public function set_body( $data ) { $this->body = $data; // Enable lazy parsing. $this->parsed_json = false; $this->parsed_body = false; $this->params['JSON'] = null; } /** * Retrieves the parameters from a JSON-formatted body. * * @since 4.4.0 * * @return array Parameter map of key to value. */ public function get_json_params() { // Ensure the parameters have been parsed out. $this->parse_json_params(); return $this->params['JSON']; } /** * Parses the JSON parameters. * * Avoids parsing the JSON data until we need to access it. * * @since 4.4.0 * @since 4.7.0 Returns error instance if value cannot be decoded. * @return true|WP_Error True if the JSON data was passed or no JSON data was provided, WP_Error if invalid JSON was passed. */ protected function parse_json_params() { if ( $this->parsed_json ) { return true; } $this->parsed_json = true; // Check that we actually got JSON. $content_type = $this->get_content_type(); if ( empty( $content_type ) || 'application/json' !== $content_type['value'] ) { return true; } $body = $this->get_body(); if ( empty( $body ) ) { return true; } $params = json_decode( $body, true ); /* * Check for a parsing error. * * Note that due to WP's JSON compatibility functions, json_last_error * might not be defined: https://core.trac.wordpress.org/ticket/27799 */ if ( null === $params && ( ! function_exists( 'json_last_error' ) || JSON_ERROR_NONE !== json_last_error() ) ) { // Ensure subsequent calls receive error instance. $this->parsed_json = false; $error_data = array( 'status' => WP_Http::BAD_REQUEST, ); if ( function_exists( 'json_last_error' ) ) { $error_data['json_error_code'] = json_last_error(); $error_data['json_error_message'] = json_last_error_msg(); } return new WP_Error( 'rest_invalid_json', __( 'Invalid JSON body passed.' ), $error_data ); } $this->params['JSON'] = $params; return true; } /** * Parses the request body parameters. * * Parses out URL-encoded bodies for request methods that aren't supported * natively by PHP. In PHP 5.x, only POST has these parsed automatically. * * @since 4.4.0 */ protected function parse_body_params() { if ( $this->parsed_body ) { return; } $this->parsed_body = true; /* * Check that we got URL-encoded. Treat a missing content-type as * URL-encoded for maximum compatibility. */ $content_type = $this->get_content_type(); if ( ! empty( $content_type ) && 'application/x-www-form-urlencoded' !== $content_type['value'] ) { return; } parse_str( $this->get_body(), $params ); /* * Amazingly, parse_str follows magic quote rules. Sigh. * * NOTE: Do not refactor to use `wp_unslash`. */ if ( get_magic_quotes_gpc() ) { $params = stripslashes_deep( $params ); } /* * Add to the POST parameters stored internally. If a user has already * set these manually (via `set_body_params`), don't override them. */ $this->params['POST'] = array_merge( $params, $this->params['POST'] ); } /** * Retrieves the route that matched the request. * * @since 4.4.0 * * @return string Route matching regex. */ public function get_route() { return $this->route; } /** * Sets the route that matched the request. * * @since 4.4.0 * * @param string $route Route matching regex. */ public function set_route( $route ) { $this->route = $route; } /** * Retrieves the attributes for the request. * * These are the options for the route that was matched. * * @since 4.4.0 * * @return array Attributes for the request. */ public function get_attributes() { return $this->attributes; } /** * Sets the attributes for the request. * * @since 4.4.0 * * @param array $attributes Attributes for the request. */ public function set_attributes( $attributes ) { $this->attributes = $attributes; } /** * Sanitizes (where possible) the params on the request. * * This is primarily based off the sanitize_callback param on each registered * argument. * * @since 4.4.0 * * @return true|WP_Error True if parameters were sanitized, WP_Error if an error occurred during sanitization. */ public function sanitize_params() { $attributes = $this->get_attributes(); // No arguments set, skip sanitizing. if ( empty( $attributes['args'] ) ) { return true; } $order = $this->get_parameter_order(); $invalid_params = array(); foreach ( $order as $type ) { if ( empty( $this->params[ $type ] ) ) { continue; } foreach ( $this->params[ $type ] as $key => $value ) { if ( ! isset( $attributes['args'][ $key ] ) ) { continue; } $param_args = $attributes['args'][ $key ]; // If the arg has a type but no sanitize_callback attribute, default to rest_parse_request_arg. if ( ! array_key_exists( 'sanitize_callback', $param_args ) && ! empty( $param_args['type'] ) ) { $param_args['sanitize_callback'] = 'rest_parse_request_arg'; } // If there's still no sanitize_callback, nothing to do here. if ( empty( $param_args['sanitize_callback'] ) ) { continue; } $sanitized_value = call_user_func( $param_args['sanitize_callback'], $value, $this, $key ); if ( is_wp_error( $sanitized_value ) ) { $invalid_params[ $key ] = $sanitized_value->get_error_message(); } else { $this->params[ $type ][ $key ] = $sanitized_value; } } } if ( $invalid_params ) { return new WP_Error( 'rest_invalid_param', sprintf( __( 'Invalid parameter(s): %s' ), implode( ', ', array_keys( $invalid_params ) ) ), array( 'status' => 400, 'params' => $invalid_params ) ); } return true; } /** * Checks whether this request is valid according to its attributes. * * @since 4.4.0 * * @return bool|WP_Error True if there are no parameters to validate or if all pass validation, * WP_Error if required parameters are missing. */ public function has_valid_params() { // If JSON data was passed, check for errors. $json_error = $this->parse_json_params(); if ( is_wp_error( $json_error ) ) { return $json_error; } $attributes = $this->get_attributes(); $required = array(); // No arguments set, skip validation. if ( empty( $attributes['args'] ) ) { return true; } foreach ( $attributes['args'] as $key => $arg ) { $param = $this->get_param( $key ); if ( isset( $arg['required'] ) && true === $arg['required'] && null === $param ) { $required[] = $key; } } if ( ! empty( $required ) ) { return new WP_Error( 'rest_missing_callback_param', sprintf( __( 'Missing parameter(s): %s' ), implode( ', ', $required ) ), array( 'status' => 400, 'params' => $required ) ); } /* * Check the validation callbacks for each registered arg. * * This is done after required checking as required checking is cheaper. */ $invalid_params = array(); foreach ( $attributes['args'] as $key => $arg ) { $param = $this->get_param( $key ); if ( null !== $param && ! empty( $arg['validate_callback'] ) ) { $valid_check = call_user_func( $arg['validate_callback'], $param, $this, $key ); if ( false === $valid_check ) { $invalid_params[ $key ] = __( 'Invalid parameter.' ); } if ( is_wp_error( $valid_check ) ) { $invalid_params[ $key ] = $valid_check->get_error_message(); } } } if ( $invalid_params ) { return new WP_Error( 'rest_invalid_param', sprintf( __( 'Invalid parameter(s): %s' ), implode( ', ', array_keys( $invalid_params ) ) ), array( 'status' => 400, 'params' => $invalid_params ) ); } return true; } /** * Checks if a parameter is set. * * @since 4.4.0 * * @param string $offset Parameter name. * @return bool Whether the parameter is set. */ public function offsetExists( $offset ) { $order = $this->get_parameter_order(); foreach ( $order as $type ) { if ( isset( $this->params[ $type ][ $offset ] ) ) { return true; } } return false; } /** * Retrieves a parameter from the request. * * @since 4.4.0 * * @param string $offset Parameter name. * @return mixed|null Value if set, null otherwise. */ public function offsetGet( $offset ) { return $this->get_param( $offset ); } /** * Sets a parameter on the request. * * @since 4.4.0 * * @param string $offset Parameter name. * @param mixed $value Parameter value. */ public function offsetSet( $offset, $value ) { $this->set_param( $offset, $value ); } /** * Removes a parameter from the request. * * @since 4.4.0 * * @param string $offset Parameter name. */ public function offsetUnset( $offset ) { $order = $this->get_parameter_order(); // Remove the offset from every group. foreach ( $order as $type ) { unset( $this->params[ $type ][ $offset ] ); } } /** * Retrieves a WP_REST_Request object from a full URL. * * @static * @since 4.5.0 * * @param string $url URL with protocol, domain, path and query args. * @return WP_REST_Request|false WP_REST_Request object on success, false on failure. */ public static function from_url( $url ) { $bits = parse_url( $url ); $query_params = array(); if ( ! empty( $bits['query'] ) ) { wp_parse_str( $bits['query'], $query_params ); } $api_root = rest_url(); if ( get_option( 'permalink_structure' ) && 0 === strpos( $url, $api_root ) ) { // Pretty permalinks on, and URL is under the API root. $api_url_part = substr( $url, strlen( untrailingslashit( $api_root ) ) ); $route = parse_url( $api_url_part, PHP_URL_PATH ); } elseif ( ! empty( $query_params['rest_route'] ) ) { // ?rest_route=... set directly $route = $query_params['rest_route']; unset( $query_params['rest_route'] ); } $request = false; if ( ! empty( $route ) ) { $request = new WP_REST_Request( 'GET', $route ); $request->set_query_params( $query_params ); } /** * Filters the request generated from a URL. * * @since 4.5.0 * * @param WP_REST_Request|false $request Generated request object, or false if URL * could not be parsed. * @param string $url URL the request was generated from. */ return apply_filters( 'rest_request_from_url', $request, $url ); } } Fortunae | Chloé Dubreuil

Fortunae

Fin du IIIème siècle. L’Empire romain est à l’aube d’un nouveau souffle, un monde en pleine mutation s’esquisse.
« Vive les Bagaudes, à bas Rome ! » crient les plus humbles dans le nord de la Gaule. Voici venu le temps de la rébellion, elle sera pleine de fureur et de sang. Tandis qu’aux portes de Lutèce, de nombreuses âmes suivent son sillage, aux marges de l’Empire, Dioclès, Commandant de la Garde impériale et futur Dioclétien, lutte pour accéder au pouvoir suprême.
A des milles de là, dans l’enceinte de la Ville la plus sacrée d’entre toutes, d’autres destinées se nouent : Albius Flaccus, jeune verrier au faîte de son art, se voit subitement contraint à l’esclavage. Le cœur de la clarissime Aleydis s’exalte pour son idéal chrétien. La muette Svenhild, autrefois discplie d’une prêtresse franque, aujourd’hui funambule d’une troupe de saltimbanques, se voit assurée par la voix de ses runes que l’amour et la liberté croiseront son chemin.
Mais les dieux sont joueurs, et s’ils faussaient le jeu pour mieux nous surprendre ?

Fondé sur des recherches rigoureuses, Fortunae fait partie de ces romans qui nous font découvrir avec passion des épisodes oubliés de la grande histoire de l’Empire romain.

Chloé Dubreuil anime des ateliers d’écriture et cours d’expression écrite en médiathèques, lycées et universités à Lyon où elle vit et écrit. Auteur de plusieurs ouvrages (romans, nouvelles, monographie),

Fortunae – De pourpre et de cendres est son troisième roman historique après Le temps d’Uranie et Yéshoua – Au nom du Fils.

 

Chroniques sorties autour de Fortunae:

Blog de Lydia :
« Passionnant ! Je me suis laissée emporter dès les premières pages dans cette histoire reprenant la grande, avec un grand H. Je ne connaissais pas les Bagaudes, ces bandes révoltées constituées de paysans gaulois et d’esclaves, qui firent trembler Rome et son empire.

Je ne ferai pas de résumé, la quatrième de couverture étant suffisamment complète. Je préfère m’attarder sur le style de Chloé Dubreuil. J’avais déjà apprécié sa plume dans Yéshoua, au nom du fils, roman sublime, original dans lequel le narrateur n’était autre que Jésus lui-même. Non plus l’entité que nous connaissons mais l’être humain, cette image de lui qui nous échappe au fond. Ici, c’est dans un tout autre registre que je la retrouve, toujours avec le même plaisir d’ailleurs. Je reconnais sa « patte ». Il a dû lui falloir des heures et des heures de travail pour s’approprier l’Histoire antique et, ce qui est fabuleux, – et qui me passionne toujours dans les bons romans historiques -, c’est que l’on ne « subit » pas un enseignement. L’écriture est si fluide que l’on se régale de tourner les pages, apprenant sans s’en rendre compte, tout un pan du patrimoine. Elle a su fondre les événements historiques dans son texte, tant et si bien que, sans la précision au début de l’ouvrage, nous ne reconnaîtrions pas les personnages réels des fictifs. Et c’est toujours le point d’achoppement, à mon sens, déterminant la réussite.

Que dire de plus ? Je crois que vous avez compris à quel point je me suis passionnée pour ce livre que je vous recommande absolument. »

Akarinthi.com :
« Si vous aimez l’histoire, si vous rêvez de connaître de l’intérieur l’Europe du III° siècle, alors je vous recommande Fortunae.

Les empereurs Carin et Numérien ne sont plus. Dioclès leur succède, mais la passation du pouvoir n’est pas aisée ; le nouvel empereur a des réformes en tête, ce qui ne plait pas à tous les patriciens. Pendant ce temps, Albius Flaccus Ravilla, jeune romain verrier, se voit brusquement vendu comme esclave par son oncle qui doit éponger ses dettes. Il échoue dans le domaine du clarissime Titus Volutianus Tertius auquel il doit deux années de sa vie. La fille de celui-ci, Aleydis, secrètement convertie au christianisme, tombe amoureuse d’Albius et espère s’unir à lui à sa libération.

Le lecteur suit l’histoire de l’Empire romain à travers l’histoire de ces différents personnages, auxquels il faut ajouter Svenhild, ancienne disciple d’une prêtresse franque devenue muette et, loin de Rome, Atuo le gaulois qui s’engage dans un groupe de brigands, les Bagaudes.

Quel lien les unit les uns aux autres ?

A l’aube de son déclin, l’Empire tremble sur ses fondations. Ses frontières sont difficiles à garder, au sein des peuples soumis gronde la révolte. Les luttes pour le pouvoir, dans toutes les strates de la société, sont sans pitié. La mort rôde. Elle fait peur, elle est une compagne fidèle sur laquelle chaque individu sait devoir compter, qu’il soit patricien, homme du peuple, esclave, gueux des provinces soumises ou soldat.

Si les luttes de pouvoir ont traversé les siècles et passionnent tout autant nos générations actuelles que celles de nos ancêtres, certaines difficultés inhérentes à l’immensité d’un pays de l’Antiquité n’existent plus aujourd’hui. A commencer par la communication. Chloé Dubreuil a fait un immense travail de recherche pour écrire Fortunae. Le lecteur découvrira la vie de Saint-Sébastien avant qu’il ne soit transpercé de flèches, apprendra les remèdes d’antan pour panser les blessures, aura une vision de la résistance à la souffrance de nos ancêtres, ira à la rencontre des Bagaudes… La minutie des détails est fantastique. A l’aide d’un vocabulaire riche, presque technique, Chloé Dubreuil s’est attachée à reconstituer un univers qui projette le lecteur deux mille ans en arrière. J’ai vu dans le développement des différents personnages un réel intérêt historique. En revanche, mise parfois en difficulté face à un vocabulaire difficile, j’aurais apprécié un lexique à la fin de l’ouvrage : il m’aurait permis de mieux comprendre la finesse de certains détails que je ne suis pas sûre d’avoir savouré à leur juste niveau.

La lecture de Fortunae pendant mes années lycée m’aurait aidée à saisir l’âme de cet empire et les difficultés de le maintenir à flot. A proposer à nos lycéens latinistes ? »

 

Extrait:
Depuis des temps immémoriaux, la parque Decima ne se lasse pas de disposer le fil sur le fuseau des destinées humaines. De ses amours avec Jupiter, le meilleur, le plus grand, ne pouvait naître qu’une reine au cœur inconstant.
« À toi donc, Fors Fortuna, déesse Fortune qui fais trembler les empereurs sous leur pourpre et s’incliner devant ton front d’airain des nations toutes entières, le droit de changer en deuil d’éminents triomphes ou d’élever des mortels de la condition la plus humble aux plus hautes gloires. »

*

Rome,
19 mars de l’année 284 sous les empereurs Carin et Numérien.

La rumeur de la plèbe ne se fait plus entendre, le jour et son dôme de nuages gris s’est évaporé ; il n’est plus question de lumière et de vie dans les entrailles de Rome. Le monde est noir, cerné par une puanteur sans nom. Noir et brumeux comme les marais du Styx. Que dissimule-t-il ? Un peuple de misère, des charognes soumises à l’appétit des rats. Albius n’aurait pas dû s’aventurer dans le Grand Egout de la ville, il n’aurait jamais dû croire que son oncle pût avoir échoué là.
Il défaille – l’odeur est insupportable -, se plie en deux pour vomir sa répugnance, ce qui éteint aussitôt la chandelle de sa lanterne. Lorsqu’il se redresse, les ténèbres lui font pousser un geignement qui se répercute sous les hautes voûtes de pierre. En tâtonnant, il réussit à trouver dans sa besace un bâtonnet soufré qu’il allume en le frottant contre le silex rangé dans son briquet. Il l’avance vers la mèche de sa chandelle. Un soupir de soulagement lui échappe. La lueur qu’elle dispense est maigre et cependant suffisante pour lui permettre d’avancer sans crainte de frôler l’un des sales rongeurs qui infestent les lieux.
Le clapotement de l’eau chargée d’immondices atténue si peu le silence oppressant… Albius Flaccus Ravilla se retourne un instant pour regarder les marches qu’il vient de descendre, luisantes d’humidité comme l’air qu’il respire. Il peut encore faire demi-tour, remonter à la

surface, pousser discrètement la grille qui s’ouvre sur le Forum et rentrer mettre à chauffer le four, tailler les blocs de verre. Il pourrait le faire, mais qu’adviendra-t-il de lui sans son oncle Vestorius à ses côtés ?
Il doit savoir si le maître verrier a réellement rejoint les mânes de leurs ancêtres. N’est-ce pas ce qu’a suggéré le tavernier auprès duquel l’artisan prend habituellement ses paris ? Le Thrace suit de près les affaires de ses clients et pour lui Vestorius ne s’est pas comporté comme attendu. Des messages ont été adressés aux comptoirs de la rue des Argentiers, l’un des tessons de poterie est revenu marqué d’une lune noire, funeste.
Par Poros, à quel expédient as-tu été conduit, mon oncle ?
Albius resserre les doigts sur le bâton dont il a pris soin de se munir. Il ne s’habituera pas à l’âcreté des lieux, elle lui picote les yeux, le fait haleter, mais après une nuit de veille, il a pris sa décision et il s’y tiendra. Cinq jours sont passés sans que son oncle ne réapparaisse. Cinq jours à attendre dans l’inquiétude, avec le pressentiment d’un bouleversement à venir. Il semble bien cette fois que les usuriers de Vestorius aient perdu patience…
Le Romain se fige. La clarté vacillante de sa lanterne ne lui a pas permis de l’apercevoir plus tôt, mais un corps boursouflé est étendu à dix coudées de lui. Les yeux gris pâles se plissent, le fin visage se tord. Sans même s’approcher, il devine que le corps n’est plus qu’un cadavre ; l’odeur de putréfaction qui s’en dégage ne laisse planer aucun doute.
Albius avance d’un pas, les jambes tremblantes ; les rats sont déjà à l’œuvre. La nausée s’invite de nouveau en lui, il a un hoquet, place vivement une main devant sa bouche avant d’expirer longuement, à petits coups hachés. Il ne tiendra pas longtemps dans ce cloaque.
Sur combien de chevaux Vestorius a-t-il encore parié ? Sa rage à manier ses monnaies l’a perdu de réputation, a fini par lasser la Chance. Et maintenant…
Le froid glacial de l’endroit tombe sur le cœur d’Albius. Il resserre les pans de son manteau sur sa tunique rapiécée, remonte sa capuche sur ses courts cheveux noirs, précocement nuancés d’une mèche grisée, et se dirige vers le cadavre. Mais des prunelles d’or surgissent brusquement de l’obscurité, faisant aussitôt s’éparpiller les rats. Albius retient son souffle, les prunelles obliquent, il aperçoit subrepticement une robe couleur brune au ras du sol avant de percevoir un couinement aigu. Une belette en chasse… Il n’attend pas, se précipite au-dessus de la dépouille de l’homme, dégage son visage du bout de sa chaussure de cuir. Le bas de sa face a disparu, des lambeaux de chair ont été arrachés, mais les furoncles qui pointent sur son front n’ont rien de commun avec son oncle.
L’envie de fuir reprend le jeune homme. Le Thrace l’a pourtant affirmé : après avoir été roué de coups, Vestorius a été jeté là deux nuits plus tôt, nécessairement là, quelque part dans ce boyau putride. Albius jette un œil à sa lanterne, la mèche de la chandelle se consume doucement. Depuis le jour où il a rasé son premier duvet, il vit dans la peur d’un moment pareil à celui-ci. Et sa peur l’a rendu lâche. Lâche comme ces millions d’âmes libres ou serviles qui susurrent « Ne nous abandonne pas ! » à un empire revenu aux origines du monde, quand l’univers entier n’était que chaos.
Albius en atteste Jupiter, il n’en sera plus de même pour lui. Les cinq années qui viennent de passer l’ont tenu au secret, il en a perdu le goût de sa ville et de ses fantaisies. Son aire de vie s’est peu à peu restreinte au seul quartier de l’Aventin où ils logent. Mais même si la pauvreté et la mort sont peu de choses sur cette terre gouvernée par les puissances d’en-haut, il ne fuira plus ; Vestorius ne lui laisse pas le choix.
Les doigts qui serrent le bâton à s’en blanchir les jointures relâchent leur prise, les yeux de cendre prennent un étrange éclat vitreux. Sans un dernier regard au cadavre, Albius s’enfonce plus avant dans les ténèbres.

 

2 réflexions au sujet de “Fortunae”

  1. Véronique Deslandres says:

    Superbe ! ça donne envie de le lire. Toutes mes félicitations Chloé, tu as effectivement une belle plume.

  2. Chloe Dubreuil says:

    Merci Véronique, tes mots me touchent… Belles fêtes de fin d’année à toi.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Required fields are marked *