diff --git a/includes/SmartLine/SmartLine.php b/includes/SmartLine/SmartLine.php
index 5e31b77..7df4e2d 100644
--- a/includes/SmartLine/SmartLine.php
+++ b/includes/SmartLine/SmartLine.php
@@ -1,558 +1,558 @@
 <?php
 
 /**
  * SmartLine 0.1
  *
  * Zed. The immensity of stars. The HyperShip. The people.
  *
  * (c) 2010, Dereckson, some rights reserved.
  * Released under BSD license.
  *
  * 0.1    2007-07-28 01:36 [DcK]    Initial release
  *        2010-07-02 00:39 [Dck]    Documentation
  *
  * @package     Zed
  * @subpackage  SmartLine
  * @author      Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
  * @copyright   2007 Sébastien Santoro aka Dereckson
  * @license     http://www.opensource.org/licenses/bsd-license.php BSD
  * @version     0.1
  * @link        http://scherzo.dereckson.be/doc/zed
  * @link        http://zed.dereckson.be/
  * @link        http://bitbucket.org/dereckson/smartline
  * @filesource
 
 ///////////////////////////////////////////////////////////////////////////////
 // SECTION I - INITIALIZATION
 ///////////////////////////////////////////////////////////////////////////////
 
 //Constants
 
 /**
  * The standard, regular output (like STDOUT on POSIX systems)
  */
 if (!defined('STDOUT')) {
     define('STDOUT', 1);
 }
 
 /**
  * The error output (like STDERR on POSIX systems)
  */
 if (!defined('STDERR')) {
     define('STDERR', -1);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // SECTION Ibis - L10n
 ///////////////////////////////////////////////////////////////////////////////
 
 //Ensures $lang is a standard array
 if (empty($lang) || !is_array($lang)) {
     $lang = [];
 }
 
 $lang = array_merge($lang, [
     //Errors
     'InvalidCommand' => "Invalid command %s. Use <strong>showcommands</strong> to show all commands.",
     'RegisteredButNotExistingCommand' => "[CRITICAL ERROR] The command %s has correctly been registered but its method or class doesn't exist.",
     'NotYetHelpForThiscommand' => "This command hasn't been documented yet.",
 
     //Help
     'DefaultHelp' => "This SmartLine is a command line interface.
                       <br /><br /><strong>showcommands</strong> prints the list.
                       <br /><strong>help &lt;command&gt;</strong> prints help for this command.",
     'Help' => [
         'help' => "<strong>help &lt;command&gt;</strong> prints command help.",
         'showcommands' => 'show available commands'
     ]
 ]);
 
 ///////////////////////////////////////////////////////////////////////////////
 // SECTION II - HELPERS FUNCTIONS
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
  * Error handler called during SmartLine command execution.
  *
  * Any error occurring during command execution will be set in STDERR.
  *
  * To get an array with all the errors:
  * <code>$errors = $yourSmartLine->gets_all(STDERR)</code>
  *
  * Or to prints all the error:
  * <code>$yourSmartLine->prints_all(STDERR)</code>
  *
  * Or to pops (gets and deletes) only the last error:
  * <code>$lastError = $yourSmartLine->gets(STDERR)</code>
  *
  * @link http://www.php.net/manual/en/function.set-error-handler.php set_error_handler, PHP manual
  * @link http://www.php.net/manual/en/errorfunc.examples.php Error handling examples, PHP manual
  *
  * @param int $level The PHP error level
  * @param string $error The error description
  * @param string $file The script where the error occurred
  * @param int $line The line where the error occurred
  */
 function SmartLineHandler($level, $error, $file, $line) {
     switch ($level) {
        case E_NOTICE:
         $type = 'Notice';
         break;
 
        case E_WARNING:
         $type = 'Warning';
         break;
 
        case E_ERROR:
         $type = 'Error';
         break;
 
        default:
         $type = "#$level";
     }
     $_SESSION['SmartLineOutput'][STDERR][] = "[PHP $type] $error in $file line $line.";
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // SECTION III - BASE CLASSES
 ///////////////////////////////////////////////////////////////////////////////
 
 //SmartLineCommand is a class implementing a SmartLine command.
 //If you want to create a more complex command, extends this class.
 
 /**
  * The SmartLine command base class.
  *
  * To add a command, create an instance of the class, like:
  * <code>
  * class HelloWorldSmartLineCommand extends SmartLineCommand {
  *   public function run ($argv, $argc) {
  *       $this->SmartLine->puts('Hello World!');
  *    }
  * }
  * </code>
  *
  * Then, registers your command:
  * <code>
  * $yourSmartLine->register_object('hello', 'HelloWorldSmartLineCommand');
  * </code>
  *
  * @see SmartLine::register_object
  */
 class SmartLineCommand {
     /**
      * Initializes a new instance of the SmartLine Command
      *
      * @param SmartLine $SmartLine the SmartLine the command belongs
      */
     public function __construct ($SmartLine) {
         $this->SmartLine = $SmartLine;
     }
 
     /**
      * Gets the command help text or indicates help should be fetched from $lang array
      *
      * @return string|bool a string containing the command help or the bool value false, to enable the default behavior (ie prints $lang['help']['nameOfTheCommand'])
      */
     public function help () {
         return false;
     }
 
     /**
      * Runs the command
      *
      * @param array $argv an array of string, each item a command argument
      * @param int $argc the number of arguments
      */
     public function run ($argv, $argc) {
 
     }
 
     static protected function parseBoolean (string $arg) : bool {
         return match (strtolower($arg)) {
             "on", "enable", "enabled", "1", "up", "true" => true,
             "off", "disable", "disabled", "0", "down", "false" => false,
 
             default => throw new InvalidArgumentException(
                 "Boolean string expected, got '$arg' instead."
             ),
         };
     }
 
     /**
      * The SmartLine where this instance of the command is registered
      *
      * @var SmartLine
      */
     public $SmartLine;
 }
 
 /**
  * This class represents a SmartLine instance
  *
  * If you use only register_object, you can use it directly.
  * If you use register_method, extends this class in your SmartLine.
  */
 class SmartLine {
     /**
      * Initializes a new instance of the SmartLine object.
      */
     public function __construct () {
         //Assumes we've an empty array where store registered commands.
         $this->commands = [];
 
         //Let's register standard commands
         $this->register_object('showcommands', 'ShowCommandsSmartLineCommand');
         $this->register_object('help', 'HelpSmartLineCommand');
     }
 
     /**
      * Registers a private method as command.
      *
      * @param string $command The name of the command to register
      * @param string $method The method to register [OPTIONAL]. If omitted, the method registered will be the method having the same name as the command.
      * @param bool $useArgvArgc If true, indicates the method uses $argv, $argc as parameters. If false, indicates the method uses its parameters (default behavior). [OPTIONAL]
      *
      * @return bool true if the command have successfully been registered ; otherwise, false.
      */
     public function register_method ($command, $method = null, $useArgvArgc = false) {
         if (is_null($function)) {
             $method = $command;
         }
 
         if (!method_exists($this, $method)) {
             $this->lastError = "Registration failed. Unknown method $method";
             return false;
         }
 
         $className = ucfirst($method) . 'SmartLineCommand';
         //If class exists, add a uniqid after function
         while (class_exists($method)) {
             $className = uniqid(ucfirst($method)) . 'SmartLineCommand';
         }
         //Creates the class
         if ($useArgvArgc) {
             $call = "$this->SmartLine->$method(\$argv, \$argc);";
         } else {
             //We don't know how many args we've, so we use call_user_func_array
             $call = "array_shift(\$argv);
 		             call_user_func_array(
 		                array(&\$this->SmartLine, '$method'),
 		                \$argv
 		             );";
         }
         $code = "class $className extends SmartLineCommand {
     public function run (\$argv, \$argc) {
         $call
     }
 }";
         eval($code);
         $this->register_object($command, $className);
         return true;
     }
 
     /**
      * Registers an object extending SmartLineCommand as command.
      *
      * @param string $command The name of the command to register
      * @param SmartLineCommand|string $object The object extending SmartLineCommand. This can be the name of the class (string) or an instance already initialized of the object (SmartLineCommand).
      * @return bool true if the command have successfully been registered ; otherwise, false.
      */
     public function register_object ($command, $object) {
         if (is_object($object)) {
             //Sets SmartLine property
             $object->SmartLine = $this;
         } elseif (is_string($object) && class_exists($object)) {
             //Creates a new instance of $object
             $object = new $object($this);
         } else {
             $this->lastError = "Registration failed. register_object second parameter must be a class name (string) or an already initialized instance of such class (object) and not a " . gettype($object);
             return false;
         }
         if (!$this->caseSensitive) {
             $command = strtolower($command);
         }
         $this->commands[$command] = $object;
         return true;
     }
 
     /**
      * Determines whether the specified command have been registered.
      *
      * @param string $command The name of the command to check
      * @return true if the specified command have been registered ; otherwise, false.
      */
     public function isRegistered ($command) {
         if (!$this->caseSensitive) {
             $command = strtolower($command);
         }
         return array_key_exists($command, $this->commands);
     }
 
     /**
      * Executes the specified expression.
      *
      * If an error occurs during the command execution:
-     *     the STDERR output will contains the errors,
+     *     the STDERR output will contain the errors,
      *     the value returned by this methods will be false.
      *
      * To execute the command and prints error:
      * <code>
      * $fooSmartLine = new SmartLine();
      * //...
      * $result = $fooSmartLine->execute($expression);
      * $fooSmartLine->prints_all();
      * if (!$result) {
      *     //Errors!
      *     echo "<h3>Errors</h3>";
      *     $fooSmartLine->prints_all(STDERR);
      * }
      * </code>
      *
      * @param string $expression The expression containing the command to execute
      * @return bool true if the command have been successfully executed ; otherwise, false.
      */
     public function execute ($expression) {
         //Does nothing if blank line
         if (!$expression) {
             return;
         }
 
         //Prepares $argv and $argc
         $argv = $this->expression2argv($expression);
         $argc = count($argv);
 
         //Gets command
         $command = $this->caseSensitive ? $argv[0] : strtolower($argv[0]);
 
         //If command doesn't exist, throws an error
         if (!array_key_exists($command, $this->commands)) {
             global $lang;
             $this->puts(sprintf($lang['InvalidCommand'], $command), STDERR);
             return false;
         }
 
         //Executes command, intercepting error and returns result
         set_error_handler("SmartLineHandler");
         try {
             $result = $this->commands[$command]->run($argv, $argc);
         } catch (Exception $ex) {
             $this->puts("<pre>$ex</pre>", STDERR);
         }
         restore_error_handler();
         return $result;
     }
 
     /**
      * Adds a message to the specified output queue.
      *
      * @param string $message the message to queue
      * @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
      */
     public function puts ($message, $output = STDOUT) {
         //
         $_SESSION['SmartLineOutput'][$output][] = $message;
     }
 
     /**
      * Truncates the specified output queue.
      *
      * @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
      */
     public function truncate ($output = STDOUT) {
         unset($_SESSION['SmartLineOutput'][$output]);
     }
 
     /**
      * Pops (gets and clears) the first message from the specified output queue.
      *
      * @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
      * @return string the message
      */
     public function gets ($output = STDOUT) {
         if ($this->count($output) > 0) {
             return array_pop($_SESSION['SmartLineOutput'][$output]);
         }
     }
 
     /**
      * Gets the number of messages in the specified output queue.
      *
      * @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
      */
     public function count ($output = STDOUT) {
         return isset($_SESSION['SmartLineOutput'][$output])
                ? count($_SESSION['SmartLineOutput'][$output])
                : 0;
     }
 
     /**
      * Gets all the message from the specified output queue.
      *
      * @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
      * @param string $prefix The string to prepend each message with. It's an optional parameter ; if omitted, '<p>'.
      * @param string $suffix The string to append each message with. It's an optional parameter ; if omitted, '</p>'.
      * @return Array an array of string, each item a message from the specified output queue
      */
     public function gets_all ($output = STDOUT, $prefix = '<p>', $suffix = '</p>') {
         $count = $this->count($output);
         if ($count == 0) {
             return;
         }
 
         $buffer = "";
         for ($i = 0 ; $i < $count ; $i++) {
             $buffer .= $prefix . $_SESSION['SmartLineOutput'][$output][$i] . $suffix;
         }
         unset ($_SESSION['SmartLineOutput'][$output]);
         return $buffer;
     }
 
     /**
      * Prints all the message from the specified output queue.
      *
      * @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
      * @param string $prefix The string to prepend each message with. It's an optional parameter ; if omitted, '<p>'.
      * @param string $suffix The string to append each message with. It's an optional parameter ; if omitted, '</p>'.
      */
     public function prints_all ($output = STDOUT, $prefix = '<p>', $suffix = '</p>') {
         $count = $this->count($output);
         if ($count == 0) {
             return;
         }
         for ($i = 0 ; $i < $count ; $i++) {
             echo $prefix, $_SESSION['SmartLineOutput'][$output][$i], $suffix;
         }
         unset ($_SESSION['SmartLineOutput'][$output]);
     }
 
     /**
      * Gets the command help
      *
      * @param string $command The command to get help from
      * @param string The command help
      */
     public function gethelp ($command) {
         return $this->commands[$command]->help();
     }
 
     /**
      * Gets an an argv array from the specified expression
      *
      * @param string $expression The expression to transform into a argv array
      * @return Array An array of string, the first item the command, the others those arguments.
      */
     private function expression2argv ($expression) {
         //Checks if expression contains "
         $pos1 = strpos($expression, '"');
 
         //We isolate "subexpression"
         if ($pos1 !== false) {
             $pos2 = $pos1;
             do {
                 $pos2 = strpos($expression, '"', $pos2 + 1);
             } while ($pos2 !== false && ($expression[$pos2 - 1] == "\\" && $expression[$pos2 - 2] != "\\"));
 
             if ($pos2 === false) {
                 //If final quote is missing, throws a warning and autoadds it.
                 $this->puts("[Warning] Final \" missing in $expression.", STDERR);
                 $argv = $this->expression2argv(substr($expression, 0, $pos1));
                 $argv[] = substr($expression, $pos1 + 1);
                 return $argv;
             }
             return array_merge(
                 $this->expression2argv(substr($expression, 0, $pos1)),
                 [substr($expression, $pos1 + 1, $pos2 - $pos1 - 1)],
                 $this->expression2argv(substr($expression, $pos2 + 1))
             );
         }
 
         //Standard expression (ie without ")
         $argv = [];
         $items = explode(' ', $expression);
         foreach ($items as $item) {
             $item = trim($item);
             if (!$item) {
                 //blank, we ignore
                 continue;
             }
             $argv[] = $item;
         }
         return $argv;
     }
 
     //The list of commands
     public array $commands = [];
 
     //Contains last error
     public $lastError = '';
 
     //If true, command isn't equal to Command
     public $caseSensitive = true;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // SECTION IV - STANDARD COMMANDS
 ///////////////////////////////////////////////////////////////////////////////
 
 /*
  * These commands are available in all default smartlines instance
  */
 
 /**
  * The standard command "showcommands"
  *
  * This command returns a list, with all the available commands
  */
 class ShowCommandsSmartLineCommand extends SmartLineCommand {
     /**
      * Runs the command
      *
      * @param array $argv an array of string, each item a command argument
      * @param int $argc the number of arguments
      */
     public function run ($argv, $argc) {
         $commands = array_keys($this->SmartLine->commands);
         sort($commands);
         $this->SmartLine->puts(implode(' ', $commands));
     }
 }
 
 /**
  * The standard command "help"
  *
  * This command prints command help.
  *
  * Help could be defined
  *    in the command classes, as a return value from the help method ;
  *    in the $lang['Help'] array, at the command key (e.g. $lang['Help']['quux'] for the quux command).
  */
 class HelpSmartLineCommand extends SmartLineCommand {
     /**
      * Runs the command
      *
      * @param array $argv an array of string, each item a command argument
      * @param int $argc the number of arguments
      */
     public function run ($argv, $argc) {
         global $lang;
         if ($argc == 1) {
             $this->SmartLine->puts($lang['DefaultHelp']);
         } elseif (!$this->SmartLine->isRegistered($argv[1])) {
             $this->SmartLine->puts(sprintf($lang['InvalidCommand'], str_replace(' ', '&nbsp;', $argv[1])), STDERR);
         } else {
             $command = strtolower($argv[1]);
             if (!$help = $this->SmartLine->gethelp($command)) {
                 if (array_key_exists($command, $lang['Help'])) {
                     $help = $lang['Help'][$command];
                 } else {
                     $help = $lang['NotYetHelpForThiscommand'];
                 }
             }
             $this->SmartLine->puts($help);
         }
     }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/includes/SmartLine/ZedCommands.php b/includes/SmartLine/ZedCommands.php
index 9a0d682..0fd355b 100644
--- a/includes/SmartLine/ZedCommands.php
+++ b/includes/SmartLine/ZedCommands.php
@@ -1,538 +1,538 @@
 <?php
 
 /**
  * Zed SmartLine commands.
  *
  * Zed. The immensity of stars. The HyperShip. The people.
  *
  * (c) 2010, Dereckson, some rights reserved.
  * Released under BSD license.
  *
  * This is the SmartLine subcontroller.
  *
  * The SmartLine is a widget allowing to add some basic CLI capability.
  *
  * It executes any command given in GET or POST request (parameter C).
  *
  * This files also provides SmartLine history helper: a method log_C to log
  * a SmartLine command and some procedural code assigning a SmartLineHistory.
  *
  * This code is inspired from Viper, a corporate PHP intranet I wrote in 2004.
  * There, the SmartLine allowed to change color theme or to find quickly user,
  * account, order or server information in a CRM context.
  *
  * @package     Zed
  * @subpackage  SmartLine
  * @author      Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
  * @copyright   2010 Sébastien Santoro aka Dereckson
  * @license     http://www.opensource.org/licenses/bsd-license.php BSD
  * @version     0.1
  * @link        http://scherzo.dereckson.be/doc/zed
  * @link        http://zed.dereckson.be/
  * @filesource
  *
  * @todo SettingsSmartLineCommand - understand why dojo floating pane isn't rendered if we est $controller instead to redirect
  */
 
 ///
 /// Register commands
 ///
 
 $smartLine->register_object('debug',    DebugSmartLineCommand::class);
 $smartLine->register_object('goto',     'GotoSmartLineCommand');
 $smartLine->register_object('guid',     'GUIDSmartLineCommand');
 $smartLine->register_object('invite',   'InviteSmartLineCommand');
 $smartLine->register_object('invites',  'InviteSmartLineCommand');
 $smartLine->register_object('list',     'ListSmartLineCommand');
 $smartLine->register_object('requests', 'RequestsSmartLineCommand');
 $smartLine->register_object('settings', 'SettingsSmartLineCommand');
 $smartLine->register_object('unixtime', 'UnixTimeSmartLineCommand');
 $smartLine->register_object('version',  'VersionSmartLineCommand');
 $smartLine->register_object('whereami', 'WhereAmISmartLineCommand');
 $smartLine->register_object('whoami',   'WhoAmISmartLineCommand');
 
 ///
 /// Help (todo: move $lang array in lang folder)
 ///
 
 $lang['Help']['debug'] = "Enable or disable debugger";
 $lang['Help']['goto'] = "Go to a location";
 $lang['Help']['guid'] = "Generate a GUID";
 $lang['Help']['invite'] = "Generate an invite. To see the generated invites, invite list.";
-$lang['Help']['list'] = "Lists specified objects (bodies, locations or places)";
+$lang['Help']['list'] = "Lists specified objects (bodies, locations, or places)";
 $lang['Help']['requests'] = "Checks if there are waiting requests";
 $lang['Help']['settings'] = 'Go to settings page';
 $lang['Help']['unixtime'] = "Prints current unixtime (seconds elapsed since 1970-01-01 00:00, UTC) or the specified unixtime date.";
 $lang['Help']['version'] = "Gets Zed's software version info (Mercurial repository version, node id and if you're on the dev or prod site)";
 $lang['Help']['whereami'] = "Where am I?";
 $lang['Help']['whoami'] = "Who am I?";
 
 /**
  * Debugger command
  */
 class DebugSmartLineCommand extends SmartLineCommand {
 
     /**
      * Runs the command
      *
      * @param array $argv an array of string, each item a command argument
      * @param int $argc the number of arguments
      */
     public function run ($argv, $argc) {
         if ($argc > 1) {
             $_SESSION['debug'] = self::parseBoolean($argv[1]);
         }
 
         $this->SmartLine->puts("Debugger " . $this->getStatus());
     }
 
     private function getStatus () : string {
         return $this->isEnabled() ? "enabled" : "disabled";
     }
 
     private function isEnabled () : bool {
         return $_SESSION['debug'] ?? false;
     }
 
 }
 
 /**
  * The goto command
  *
  * Moves to the current perso to the specified location.
  */
 class GotoSmartLineCommand extends SmartLineCommand {
     /**
      * Runs the command
      *
      * @param array $argv an array of string, each item a command argument
      * @param int $argc the number of arguments
      *
      * @todo allow .goto global local (e.g. .goto B0001001 T2C3)
      * @todo determine if we allow rewrite rules to bypass can_travel rules
      */
     public function run ($argv, $argc) {
         global $CurrentPerso;
 
         if ($argc == 1) {
             $this->SmartLine->puts("Where do you want to go?", STDERR);
             return;
         }
 
         if ($argc > 2) {
             $ignored_string = implode(" ", array_slice($argv, 2));
             $this->SmartLine->puts("Warning: ignoring $ignored_string", STDERR);
         }
 
         require_once("includes/geo/location.php");
         require_once("includes/travel/travel.php");
 
         $here = new GeoLocation($CurrentPerso->location_global, $CurrentPerso->location_local);
         $travel = Travel::load(); //maps content/travel.xml
 
         //Parses the expression, by order of priority, as :
         //  - a rewrite rule
         //  - a new global location
         //  - a new local location (inside the current global location)
         if (!$travel->try_parse_rewrite_rule($argv[1], $here, $place)) {
             try {
                 $place = new GeoLocation($argv[1]);
 
                 if ($place->equals($CurrentPerso->location_global)) {
                     $this->SmartLine->puts("You're already there.");
                     return;
                 }
             } catch (Exception $ex) {
                 //Global location failed, trying local location
                 try {
                     $place = new GeoLocation($CurrentPerso->location_global, $argv[1]);
                 } catch (Exception $ex) {
                     $this->SmartLine->puts($ex->getMessage(), STDERR);
                     return;
                 }
 
                 if ($place->equals($here)) {
                     $this->SmartLine->puts("You're already there.");
                     return;
                 }
             }
         }
 
         //Could we really go there?
         if (!$travel->can_travel($here, $place)) {
             $this->SmartLine->puts("You can't reach that location.");
             return;
         }
 
         //Moves
         $CurrentPerso->move_to($place->global, $place->local);
         $this->SmartLine->puts("You travel to that location.");
         return;
     }
 }
 
 /**
  * The GUID command
  *
  * Prints a new GUID.
  *
  * guid 8 will print 8 guid
  */
 class GUIDSmartLineCommand extends SmartLineCommand {
     /**
      * Runs the command
      *
      * @param array $argv an array of string, each item a command argument
      * @param int $argc the number of arguments
      */
     public function run ($argv, $argc) {
         if ($argc > 1 && is_numeric($argv[1])) {
             for ($i = 0 ; $i < $argv[1] ; $i++) {
                  $this->SmartLine->puts(new_guid());
             }
             return;
         }
 
         $this->SmartLine->puts(new_guid());
     }
 }
 
 /**
  * The invite command
  *
  * Manages invites.
  *
  * invite [add]
  *     creates a new invite code
  *
  * invite del <invite code>
  *     deletes the specified invite
  *
  * invite list
  *     prints current invite codes
  */
 class InviteSmartLineCommand extends SmartLineCommand {
     /**
      * Runs the command
      *
      * @param array $argv an array of string, each item a command argument
      * @param int $argc the number of arguments
      */
     public function run ($argv, $argc) {
         require_once('includes/objects/invite.php');
         global $CurrentUser, $CurrentPerso;
 
         $command = ($argc > 1) ? strtolower($argv[1]) : '';
         switch ($command) {
             case 'list':
                 $codes = Invite::get_invites_from($CurrentPerso->id);
                 if (!count($codes)) {
                     $this->SmartLine->puts("No invite code.");
                 } else {
                     foreach ($codes as $code) {
                         $this->SmartLine->puts($code);
                     }
                 }
                 break;
 
             case 'add':
             case '':
                 $code = Invite::create($CurrentUser->id, $CurrentPerso->id);
                 $url = get_server_url() . get_url('invite', $code);
                 $this->SmartLine->puts("New invite code created: $code<br />Invite URL: $url");
                 break;
 
             case 'del':
                 $code = $argv[2];
                 if (!preg_match("/^([A-Z]){3}([0-9]){3}$/i", $code)) {
                     $this->SmartLine->puts("Invalid code format. Use invite list to get all your invite codes.", STDERR);
                 } else {
                     $invite = new Invite($code);
                     if ($CurrentPerso->id == $invite->from_perso_id) {
                         $invite->delete();
                         $this->SmartLine->puts("Deleted");
                     } else {
                         $this->SmartLine->puts("Invalid code. Use invite list to get all your invite codes.", STDERR);
                     }
                 }
                 break;
 
             default:
                 $this->SmartLine->puts("Usage: invite [add|list|del <code>]", STDERR);
                 break;
         }
 
     }
 }
 
 /**
  * The list command
  *
  * Prints a list of bodies, locations or places.
  *
  * This can easily be extended to output any list from any table.
  */
 class ListSmartLineCommand extends SmartLineCommand {
     /**
      * Runs the command
      *
      * @param array $argv an array of string, each item a command argument
      * @param int $argc the number of arguments
      */
     public function run ($argv, $argc) {
         if ($argc == 1) {
             $this->SmartLine->puts("Available lists: bodies, locations, places");
             return;
         }
 
         switch ($objects = $argv[1]) {
             case 'bodies':
                 $list = $this->get_list(TABLE_BODIES, "CONCAT('B', body_code)", "body_name");
                 $this->SmartLine->puts($list);
                 break;
 
             case 'locations':
                 $list = $this->get_list(TABLE_LOCATIONS, "location_code", "location_name");
                 $this->SmartLine->puts($list);
                 break;
 
             case 'places':
                 if ($argv[2] == "-a" || $argv[2] == "--all") {
                     //Global bodies places list
                     $list = $this->get_list(TABLE_PLACES, "CONCAT('B', body_code, place_code)", "place_name");
                 } else {
                     //Local places (or equivalent) list
                     global $CurrentPerso;
                     switch ($CurrentPerso->location_global[0]) {
                         case 'B':
                             $body_code = substr($CurrentPerso->location_global, 1, 5);
                             $list = $this->get_list(TABLE_PLACES, "CONCAT('B', body_code, place_code)", "place_name", "body_code = $body_code");
                             break;
 
                         case 'S':
                             $this->SmartLine->puts("I don't have a map of the spaceship.", STDERR);
                             return;
 
                         default:
                             $this->SmartLine->puts("Unknown location type. Can only handle B or S.", STDERR);
                             return;
                     }
                 }
                 $this->SmartLine->puts($list);
                 break;
 
             default:
                 $this->SmartLine->puts("Unknown objects to list: $objects", STDERR);
         }
 
     }
 
     /**
      * Gets a custom list from the specified table and fields.
      *
      * The list will be sorted by the specified key, using ascending order.
      *
      * @param $table the table to query from the database
      * @param $key the first field to fetch, as key
      * @param $value the second field to fetch, as value
      * @param $where the WHERE clause, without the WHERE keyword (optional)
      */
     public function get_list ($table, $key, $value, $where = null) {
         global $db;
         $sql = "SELECT $key as `key`, $value as value FROM $table ";
         if ($where) {
             $sql .= "WHERE $where ";
         }
         $sql .= "ORDER BY `key` ASC";
         if (!$result = $db->query($sql)) {
             message_die(SQL_ERROR, "Unable to fetch list", '', __LINE__, __FILE__, $sql);
         }
         while ($row = $db->fetchRow($result)) {
             $rows .= "<tr><td>$row[key]</td><td>$row[value]</td></tr>";
         }
         $this->SmartLine->truncate(STDERR); //kludge
         return "<table cellspacing=\"8\"><thead style=\"color: white\" scope=\"row\"><tr><th>Key</th><th>Value</th></thead><tbody>$rows</tbody></table>";
     }
 }
 
 
 /**
  * The requests command
  *
- * Redirects user the the requests page.
+ * Redirects user to the requests page.
  *
  * By default only redirect if a flag indicates there's a new request.
  *
  * To forcefully goes to the request page, requests --force
  */
 class RequestsSmartLineCommand extends SmartLineCommand {
     /**
      * Runs the command
      *
      * @param array $argv an array of string, each item a command argument
      * @param int $argc the number of arguments
      */
     public function run ($argv, $argc) {
         global $CurrentPerso;
         $force = ($argc > 1) && ($argv[1] == "-f" || $argv[1] == "--force");
         if ($force || (array_key_exists('site.requests', $CurrentPerso->flags) && $CurrentPerso->flags['site.requests'])) {
             global $controller;
             $controller = 'controllers/persorequest.php';
         } else {
             $this->SmartLine->puts("No request waiting.");
         }
     }
 }
 
 /**
  * The settings command
  *
- * Redirects user the the settings page.
+ * Redirects user to the settings page.
  */
 class SettingsSmartLineCommand extends SmartLineCommand {
     /**
      * Runs the command
      *
      * @param array $argv an array of string, each item a command argument
      * @param int $argc the number of arguments
      */
     public function run ($argv, $argc) {
         if (headers_sent()) {
             global $controller;
             $controller = 'controllers/settings.php';
         } else {
             header('location: ' . get_url('settings'));
         }
     }
 }
 
 /**
  * The unixtime command
  *
  * Prints current unixtime (seconds elapsed since 1970-01-01 00:00, UTC)
  * or if an unixtime is specified as argument, the matching date.
  */
 class UnixTimeSmartLineCommand extends SmartLineCommand {
     /**
      * Runs the command
      *
      * @param array $argv an array of string, each item a command argument
      * @param int $argc the number of arguments
      */
     public function run ($argv, $argc) {
         date_default_timezone_set('UTC');
         if ($argc == 1) {
             $this->SmartLine->puts(time());
         } elseif ($argc == 2 && is_numeric($argv[1])) {
             $this->SmartLine->puts(strftime("%Y-%m-%d %X", $argv[1]));
             $this->SmartLine->puts(get_hypership_time($argv[1]));
         } else {
             array_shift($argv);
             $date = implode(' ', $argv);
             if ($time = strtotime($date) !== false) {
                 $this->SmartLine->puts("Unixtime from $date: <span class=\"highlight\">$time</span>");
             } else {
                 $this->SmartLine->puts("$date isn't a unixtime nor a valid date strtotime is able to parse.", STDERR);
             }
         }
     }
 }
 
 /**
  * The version command
  *
  * Prints current hg revision, if we're in prod or dev environment and
  * the current revision's hash.
  *
  * The version and env information is extracted from
  *      .hg/tags.cache (indicating we're in a Mercurial repo and so in a dev environment), or from
  *      version.txt file (indicating we've deployed code in a production environment)
  *
  * e.g. r130 (development environment)
  *      Hash: 057bf394741706fd2136541e3bb07c9e60b4963d
  */
 class VersionSmartLineCommand extends SmartLineCommand {
     private static function getGitHash (string $gitFolder = '.git') : string {
         $head = trim(file_get_contents("$gitFolder/HEAD"));
 
         if (str_starts_with($head, "ref: ")) {
              // Follows reference
              $ref = substr($head, 5);
              return file_get_contents("$gitFolder/$ref");
         }
 
         return $head;
     }
 
     /**
      * Runs the command
      *
      * @param array $argv an array of string, each item a command argument
      * @param int $argc the number of arguments
      */
     public function run ($argv, $argc) {
         if (file_exists('.hg/tags.cache')) {
             //Gets .hg revision
             $content = file_get_contents('.hg/tags.cache');
             $info = explode(' ', $content, 2);
             $info[] = "development environment";
 
             $this->SmartLine->puts("r$info[0] ($info[2])");
             $this->SmartLine->puts("Hash: $info[1]");
         } elseif (file_exists('.git/HEAD')) {
             $hash = self::getGitHash();
             $this->SmartLine->puts("Hash: $hash");
         } elseif (file_exists('version.txt')) {
             $content = file('version.txt');
             foreach ($content as $line) {
                 $this->SmartLine->puts($line);
             }
         } else {
             $this->SmartLine->puts("No version information available.", STDERR);
             return false;
         }
 
         return true;
     }
 }
 
 /**
  * The whereami (Where am I?) command
  *
  * Prints current position, e.g. B00001001 - Tour, Hypership
  */
 class WhereAmISmartLineCommand extends SmartLineCommand {
     /**
      * Runs the command
      *
      * @param array $argv an array of string, each item a command argument
      * @param int $argc the number of arguments
      */
     public function run ($argv, $argc) {
         global $CurrentPerso;
 
         require_once("includes/geo/location.php");
         $place = new GeoLocation($CurrentPerso->location_global);
         $this->SmartLine->puts($CurrentPerso->location_global . ' - ' . $place);
     }
 }
 
 
 /**
  * The whoami (Who am I?) command
  *
  * Prints current position, e.g. B00001001 - Tour, Hypership
  */
 class WhoAmISmartLineCommand extends SmartLineCommand {
     /**
      * Runs the command
      *
      * @param array $argv an array of string, each item a command argument
      * @param int $argc the number of arguments
      */
     public function run ($argv, $argc) {
         global $CurrentPerso;
         $reply = "<span id=\"whoami.nickname\">$CurrentPerso->nickname</span> (<span id=\"whoami.name\">$CurrentPerso->name</span>), <span id=\"whoami.race\">$CurrentPerso->race</span>";
         $this->SmartLine->puts($reply);
     }
 }
diff --git a/includes/api/cerbere.php b/includes/api/cerbere.php
index 666706d..8c48823 100644
--- a/includes/api/cerbere.php
+++ b/includes/api/cerbere.php
@@ -1,125 +1,125 @@
 <?php
 
 /**
  * API security
  *
  * Zed. The immensity of stars. The HyperShip. The people.
  *
  * (c) 2010, Dereckson, some rights reserved.
  * Released under BSD license.
  *
  * This file provides a cerbere function, to assert the user is correctly
  * authenticated in the API call.
  *
  * @package     Zed
  * @subpackage  API
  * @author      Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
  * @copyright   2010 Sébastien Santoro aka Dereckson
  * @license     http://www.opensource.org/licenses/bsd-license.php BSD
  * @version     0.1
  * @link        http://scherzo.dereckson.be/doc/zed
  * @link        http://zed.dereckson.be/
  * @filesource
  */
 
 /**
  * Determines if localhost calls could be passed.
  *
  * If true, any call from localhost is valid. Otherwise, normal security rules are applied.
  */
 define('ALLOW_LOCALHOST', false);
 
 /**
  * Determines if error should be printed.
  *
  * If true, the error will be printed according the FORMAT_ERROR setting. Otherwise, a blank page will be served.
  */
 define('OUTPUT_ERROR', true);
 
 /**
  * Determines if the error must be formatted.
  *
  * If true, any error will be sent to api_output ; otherwise, it will be printed as is.
  */
 define('FORMAT_ERROR', false);
 
 
 if (!defined('TABLE_API_KEYS')) {
     /**
      * The table where are located the API keys
      */
     define('TABLE_API_KEYS', 'api_keys');
 }
 
 /**
  * Checks if credentials are okay and exits if not
  *
- * If the credentials aren't valid, it will prints an error message if
+ * If the credentials aren't valid, it will print an error message if
  * OUTPUT_ERROR is defined and true.
  *
  * This error message will be formatted through the api_output function if
  * FORMAT_ERROR is defined and true ; otherwise, it will be print as is.
  *
  * To help debug, you can also define ALLOW_LOCALHOST. If this constant is
  * defined and true, any call from localhost will be accepted, without checking
  * the key.
  *
  * @see cerbere_die
  */
 function cerbere () {
     //If ALLOW_LOCALHOST is true, we allow 127.0.0.1 queries
     //If you use one of your local IP in your webserver vhost like 10.0.0.3
     //it could be easier to create yourself a test key
     if (ALLOW_LOCALHOST && $_SERVER['REMOTE_ADDR'] == '127.0.0.1') {
         return;
     }
 
     $guid = $_REQUEST['key'] ?? "";
 
     //No key, no authentication
     if ($guid === "") {
         cerbere_die('You must add credentials to your request.');
     }
 
     //Authenticates user
     global $db;
     $guid = $db->escape($guid);
     $sql = "SELECT key_active FROM " . TABLE_API_KEYS .
            " WHERE key_guid like '$guid'";
     if (!$result = $db->query($sql)) {
         message_die(SQL_ERROR, "Can't get key", '', __LINE__, __FILE__, $sql);
     }
     if ($row = $db->fetchRow($result)) {
         if ($row['key_active']) {
             //key_hits++
             $sql = "UPDATE " . TABLE_API_KEYS . " SET key_hits = key_hits + 1" .
                    " WHERE key_guid like '$guid'";
             if (!$db->query($sql)) {
                 message_die(SQL_ERROR, "Can't record api call", '', __LINE__, __FILE__, $sql);
             }
         } else {
             cerbere_die("Key disabled.");
         }
     } else {
         cerbere_die("Key doesn't exist.");
     }
 }
 
 /**
  * Prints a message in raw or API format, then exits.
  *
  * The error message will be formatted through api_output if the constant
  * FORMAT_ERROR is defined and true. Otherwise, it will be printed as is.
  *
  * @param string $message The error message to print
  */
 function cerbere_die ($message) {
     if (OUTPUT_ERROR) {
         if (FORMAT_ERROR) {
             api_output($message, 'error');
         } else {
             echo $message;
         }
     }
     exit;
 }
diff --git a/includes/auth/IAuthentication.php b/includes/auth/IAuthentication.php
index 9456389..213fd93 100644
--- a/includes/auth/IAuthentication.php
+++ b/includes/auth/IAuthentication.php
@@ -1,55 +1,55 @@
 <?php
 
 /**
  * Authentication method interface.
  *
  * Zed. The immensity of stars. The HyperShip. The people.
  *
  * (c) 2013, Dereckson, some rights reserved.
  * Released under BSD license.
  *
  * @package     Zed
  * @subpackage  Auth
  * @author      Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
  * @copyright   2013 Sébastien Santoro aka Dereckson
  * @license     http://www.opensource.org/licenses/bsd-license.php BSD
  * @link        http://scherzo.dereckson.be/doc/zed
  * @link        http://zed.dereckson.be/
  * @filesource
  */
 
 /**
  * Authentication method interface.
  */
 interface IAuthentication {
     /**
      * Determines if an user has been authenticated.
      *
      * @return boolean true if the user has successfully been authenticated; otherwise, false.
      */
     public function isValid ();
 
     /**
      * Gets the last authentication error
      *
      * @return string The last authentication error
      */
     public function getError ();
 
     /**
      * Gets the user_id matching the authentication method
      *
      * @return int the authenticated user ID
      */
     public function getUserID ();
 
     /**
      * Determines if the next authentication method could be tried if this one failed.
      *
-     * This allow when a method has failed in such a way the user must be warned to warn it,
+     * This allows when a method has failed in such a way the user must be warned to warn it,
      * returning false.
      *
      * @return bool true if authentication can go on to the next method; otherwise, false
      */
     public function canTryNextAuthenticationMethod ();
 }
diff --git a/includes/auth/YubiCloudAuthentication.php b/includes/auth/YubiCloudAuthentication.php
index a9554ca..9ee2d58 100644
--- a/includes/auth/YubiCloudAuthentication.php
+++ b/includes/auth/YubiCloudAuthentication.php
@@ -1,192 +1,192 @@
 <?php
 
 /**
  * YubiCloud authentication class.
  *
  * Zed. The immensity of stars. The HyperShip. The people.
  *
  * (c) 2013, Dereckson, some rights reserved.
  * Released under BSD license.
  *
  * @package     Zed
  * @subpackage  Auth
  * @author      Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
  * @copyright   2013 Sébastien Santoro aka Dereckson
  * @license     http://www.opensource.org/licenses/bsd-license.php BSD
  * @link        http://scherzo.dereckson.be/doc/zed
  * @link        http://zed.dereckson.be/
  * @filesource
  */
 
 require_once('Auth/Yubico.php');
 
 /**
  * YubiCloudAuthentication class
  *
  * Authenticates a user through YubiCloud
  */
 class YubiCloudAuthentication implements IAuthentication {
     /**
      * The key
      * @var string
      */
     private $key;
 
     /**
      * The username who should match the key
      * @var string
      */
     private $username;
 
     /**
      * The user_id
      * @var int
      */
     private $user_id;
 
     /**
      * Indicates if the error MUST be returned to the user
      * @var string
      */
     private $mustThrowError = false;
 
     /**
      * The last validation error
      * @var string
      */
     public $error;
 
     /**
      * Initializes a new instance of the key
      *
      * @param string $key The key
      */
     public function __construct ($key, $username = null) {
         $this->username = $username;
         $this->key = $key;
     }
 
     /**
      * Validates the specified key's characters to determine if it looks like an OTP
      *
      * @return boolean true if the input seems an OTP key; otherwise, false.
      */
     function looksValidOTP () {
         return preg_match("/^[cbdefghijklnrtuv]{32,48}$/", $this->key);
     }
 
     /**
      * Gets public identity
      *
      * @return string Public identity
      */
     function getPublicIdentity () {
         return substr($this->key, 0, 12);
     }
 
     /**
      * Validates an OTP key against the YubiCloud servers
      *
      * @return boolean true if the input is a valid OTP key; otherwise, false.
      */
     function isValid () {
         global $Config;
 
         //No need to lost time to query server if format is incorrect.
         if (!$this->looksValidOTP()) {
             $this->error = "Not the expected YubiKey OTP format.";
             return false;
         }
 
         //Query YubiCloud. We stop validation tests if that fails.
         $yubi = new Auth_Yubico(
             $Config['YubiCloud']['ClientID'],
             $Config['YubiCloud']['SecretKey']
         );
         $auth = $yubi->verify($this->key);
         if (@PEAR::isError($auth)) {
             $this->error = $auth->getMessage();
             return false;
         }
 
         //Note: We first query the YubiCloud server, then we check if we can use the key
-        //      as the key is an OTP (*one time* password), this allow to invalidate it.
+        //      as the key is an OTP (*one time* password), this allows to invalidate it.
         //      If we wouldn't do that, an attacker can reuse this password for another site.
         if (!$this->computeUserID()) {
             $this->error = "Valid YubiKey OTP. But the key doesn't match any account.";
             $this->mustThrowError = true;
             return false;
         }
 
         //Finally, if someone puts also a login, we'll check this user ID match this username
         if ($this->username) {
             $user = User::get($this->user_id);
             if ($this->username != $user->name) {
             $this->error = "Valid YubiKey OTP but fix or remove your username.";
             $this->mustThrowError = true;
             return false;
             }
         }
 
         return true;
     }
 
     /**
      * Gets the user_id matching the username
      *
      * You first need to validate the username, calling the isValid method.
      */
     function computeUserID () {
         global $db;
 
         /**
          * Here a MySQL record for a valid OTP
          * +---------+-----------+---------------+-----------------+---------+
          * | auth_id | auth_type | auth_identity | auth_properties | user_id |
          * +---------+-----------+---------------+-----------------+---------+
          * |       2 | YubiKey   | cccccccccccc  | NULL            |    1234 |
          * +---------+-----------+---------------+-----------------+---------+
          */
         $authentication_identity = $this->getPublicIdentity();
         $sql = "SELECT user_id FROM " . TABLE_USERS_AUTH
              . " WHERE auth_type = 'YubiKey' AND auth_identity = '$authentication_identity'";
         if (!$result = $db->query($sql)) {
             message_die(SQL_ERROR, "Can't query users authentication table.", '', __LINE__, __FILE__, $sql);
         }
         if ($row = $db->fetchRow($result)) {
             $this->user_id = $row['user_id'];
             return true;
         }
         return false;
     }
 
     /**
      * Gets the last authentication error
      *
      * @return string The last authentication error
      */
     function getError () {
         return $this->error;
     }
 
     /**
      * Gets the user_id matching the key
      *
      * You first need to query the authentication table, calling the computeUserID method.
      * This is automatically done by IsValid, as we need to validate key matches someone.
      *
      * @return int the user ID
      */
     function getUserID () {
         return $this->user_id;
     }
 
     /**
      * Determines if the next authentication method could be tried if this one failed.
      *
      * @return bool true if authentication can go on to the next method; otherwise, false
      */
     function canTryNextAuthenticationMethod () {
         return !$this->mustThrowError;
     }
 }
diff --git a/includes/cache/cache.php b/includes/cache/cache.php
index 46f59ef..d9cde11 100644
--- a/includes/cache/cache.php
+++ b/includes/cache/cache.php
@@ -1,89 +1,89 @@
 <?php
 
 /**
  * Cache calling class.
  *
  * Zed. The immensity of stars. The HyperShip. The people.
  *
  * (c) 2010, Dereckson, some rights reserved.
  * Released under BSD license.
  *
  * This file provides a calling class, which read the configuration, ensures
  * the cache class for the cache engine given in config exists and initializes
  * it.
  *
  * You'll find a sample of implementation in the CacheMemcached.
  * @see CacheMemcached
  *
  * If no caching mechanism, a "blackhole" void cache will be used.
  * @see CacheVoid.
  *
  * The class to call is determined from the following preference:
  * <code>
  * $Config['cache']['engine'] = 'memcached'; //will use CacheMemcached class.
  * </code>
  *
  * 0.1    2010-07-06 22:45    Initial version [DcK]
  *
  * @package     Zed
  * @subpackage  Cache
  * @author      Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
  * @copyright   2010 Sébastien Santoro aka Dereckson
  * @license     http://www.opensource.org/licenses/bsd-license.php BSD
  * @version     0.1
  * @link        http://scherzo.dereckson.be/doc/zed
  * @link        http://zed.dereckson.be/
  * @filesource
  */
 
 /**
  * Cache caller
  */
 class Cache {
     /**
      * Gets the cache instance, initializing it if needed
      *
      * The correct cache instance to initialize will be determined from the
      * $Config['cache']['engine'] preference.
      *
      * The class cache to use will be Cache + (preference engine, capitalized)
      *
-     * This method will creates an instance of the specified object,
+     * This method will create an instance of the specified object,
      * calling the load static method from this object class.
      *
      * Example:
      * <code>
      * $Config['cache']['engine'] = 'quux';
      * $cache = Cache::load(); //Cache:load() will call CacheQuux:load();
      * </code>
      *
      * @return Cache the cache instance, or null if nothing is cached
      */
     static function load () {
         global $Config;
         if (
             !array_key_exists('cache', $Config) ||
             !array_key_exists('engine', $Config['cache'])
         ) {
             //cache is not configured or engine is not specified
             $engine = 'void';
         } else {
             //engine is specified in the configuration
             $engine = $Config['cache']['engine'];
         }
 
         $engine_file = 'includes/cache/' . $engine . '.php';
         $engine_class = 'Cache' . ucfirst($engine);
 
         if (!file_exists($engine_file)) {
             message_die(GENERAL_ERROR, "Can't initialize $engine cache engine.<br />$engine_file not found.", 'Cache');
         }
 
         require_once($engine_file);
         if (!class_exists($engine_class)) {
             message_die(GENERAL_ERROR, "Can't initialize $engine cache engine.<br />$engine_class class not found.", 'Cache');
         }
 
         return call_user_func([$engine_class, 'load']);
     }
 }
diff --git a/includes/config.php b/includes/config.php
index a3298c2..c3a69a8 100644
--- a/includes/config.php
+++ b/includes/config.php
@@ -1,297 +1,297 @@
 <?php
 
 /**
  * Autogenerable configuration file
  *
  * Zed. The immensity of stars. The HyperShip. The people.
  *
  * (c) 2010, Dereckson, some rights reserved.
  * Released under BSD license.
  *
  * @package     Zed
  * @subpackage  Keruald
  * @author      Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
  * @copyright   2010 Sébastien Santoro aka Dereckson
  * @license     http://www.opensource.org/licenses/bsd-license.php BSD
  * @version     0.1
  * @link        http://scherzo.dereckson.be/doc/zed
  * @link        http://zed.dereckson.be/
  * @filesource
  */
 
 use Dotenv\Dotenv;
 
 use Keruald\OmniTools\OS\Environment;
 use Keruald\Database\Engines\MySQLiEngine;
 
 ////////////////////////////////////////////////////////////////////////////////
 ///                                                                          ///
 /// 0. DotEnv - read environment from .env                                   ///
 ///                                                                          ///
 ////////////////////////////////////////////////////////////////////////////////
 
 function loadEnvironment() {
     $directory = dirname(__DIR__);
     $dotenv = Dotenv::createImmutable($directory);
     $dotenv->safeLoad();
 }
 
 loadEnvironment();
 
 ////////////////////////////////////////////////////////////////////////////////
 ///                                                                          ///
 /// I. SQL configuration                                                     ///
 ///                                                                          ///
 ////////////////////////////////////////////////////////////////////////////////
 
 //SQL configuration
 $Config['database']['engine'] = MySQLiEngine::class;
 $Config['database']['host'] = $_ENV["DB_HOST"] ?? "localhost";
 $Config['database']['username'] = $_ENV["DB_USER"] ?? "zed";
 $Config['database']['password'] = $_ENV["DB_PASSWORD"] ?? "zed";
 $Config['database']['database'] = $_ENV["DB_NAME"] ?? "zed";
 $Config['database']['dontThrowExceptions'] = true;
 
 //SQL tables
 $prefix = '';
 define('TABLE_API_KEYS', $prefix . 'api_keys');
 define('TABLE_COMMENTS', $prefix . 'comments');
 define('TABLE_CONTENT_FILES', $prefix . 'content_files');
 define('TABLE_CONTENT_LOCATIONS', $prefix . 'content_locations');
 define('TABLE_CONTENT_ZONES', $prefix . 'content_zones');
 define('TABLE_CONTENT_ZONES_LOCATIONS', $prefix . 'content_zones_locations');
 define('TABLE_LOG', $prefix . 'log');
 define('TABLE_LOG_SMARTLINE', $prefix . 'log_smartline');
 define('TABLE_MESSAGES', $prefix . 'messages');
 define('TABLE_MOTD', $prefix . 'motd');
 define('TABLE_PAGES', $prefix . 'pages');
 define('TABLE_PAGES_EDITS', $prefix . 'pages_edits');
 define('TABLE_PERSOS', $prefix . 'persos');
 define('TABLE_PERSOS_FLAGS', $prefix . 'persos_flags');
 define('TABLE_PERSOS_NOTES', $prefix . 'persos_notes');
 define('TABLE_PORTS', $prefix . 'ports');
 define('TABLE_PROFILES', $prefix . 'profiles');
 define('TABLE_PROFILES_COMMENTS', $prefix . 'profiles_comments');
 define('TABLE_PROFILES_PHOTOS', $prefix . 'profiles_photos');
 define('TABLE_PROFILES_TAGS', $prefix . 'profiles_tags');
 define('TABLE_REGISTRY', $prefix . 'registry');
 define('TABLE_REQUESTS', $prefix . 'requests');
 define('TABLE_REQUESTS_REPLIES', $prefix . 'requests_replies');
 define('TABLE_SESSIONS', $prefix . 'sessions');
 define('TABLE_SHIPS', $prefix . 'ships');
 define('TABLE_USERS', $prefix . 'users');
 define('TABLE_USERS_INVITES', $prefix . 'users_invites');
 define('TABLE_USERS_AUTH', $prefix . 'users_auth');
 
 //Geo tables
 define('TABLE_BODIES', $prefix . 'geo_bodies');
 define('TABLE_LOCATIONS', $prefix . 'geo_locations');   //Well... it's a view
 define('TABLE_PLACES', $prefix . 'geo_places');
 
 ////////////////////////////////////////////////////////////////////////////////
 ///                                                                          ///
 /// II. Site configuration                                                   ///
 ///                                                                          ///
 ////////////////////////////////////////////////////////////////////////////////
 
 //Default theme
 $Config['DefaultTheme'] = "Zed";
 
 //Dates
 date_default_timezone_set("UTC");
 
 //Secret key, used for some verification hashes in URLs or forms.
 $Config['SecretKey'] = $_ENV["ZED_SECRET_KEY"] ?? 'Lorem ipsum dolor';
 
 //When reading files, buffer size
 define('BUFFER_SIZE', 4096);
 
 ////////////////////////////////////////////////////////////////////////////////
 ///                                                                          ///
 /// III. Script URLs                                                         ///
 ///                                                                          ///
 ////////////////////////////////////////////////////////////////////////////////
 
 /*
  * Apache httpd, without mod_rewrite:
  *
  *   Subdirectory:
  *     - $Config['SiteURL'] = 'http://zed.dereckson.be/hypership/index.php';
  *     - $Config['BaseURL'] = '/hypership/index.php';
  *
  *   Root directory:
  *     - $Config['SiteURL'] = 'http://zed.dereckson.be/index.php';
  *     - $Config['BaseURL'] = '/index.php';
  *
  * Apache httpd, with mod_rewrite:
  *
  *   Subdirectory:
  *     - $Config['SiteURL'] = 'http://zed.dereckson.be/hypership';
  *     - $Config['BaseURL'] = '/hypership';
  *
  *     In .htaccess or your vhost definition:
  *       RewriteEngine On
  *       RewriteBase /hypership/
  *       RewriteCond %{REQUEST_FILENAME} !-f
  *       RewriteCond %{REQUEST_FILENAME} !-d
  *       RewriteRule . /hypership/index.php [L]
  *
  *   Root directory:
  *     - $Config['SiteURL'] = 'http://zed.dereckson.be';
  *     - $Config['BaseURL'] = '';
  *
  *     In .htaccess or your vhost definition:
  *       RewriteEngine On
  *       RewriteBase /
  *       RewriteCond %{REQUEST_FILENAME} !-f
  *       RewriteCond %{REQUEST_FILENAME} !-d
  *       RewriteRule . /index.php [L]
  *
  * nginx:
  *
  *   Use same config.php settings than Apache httpd, with mod_rewrite.
  *
  *   In your server block:
  *       location / {
- *           #Serves static files if they exists, with one month cache
+ *           #Serves static files if they exist, with one month cache
  *           if (-f $request_filename) {
  *               expires 30d;
  *               break;
  *           }
  *
- *           #Sends all non existing file or directory requests to index.php
+ *           #Sends all non-existing file or directory requests to index.php
  *           if (!-e request_filename) {
  *               rewrite ^(.+)$ /index.php last;
  *               #Or if you use a subdirectory:
  *               #rewrite ^(.+)$ /hypership/index.php last;
  *           }
  *       }
  *
  *       location ~ \.php$ {
  *           #Your instructions to pass query to your FastCGI process, like:
  *           fastcgi_pass   127.0.0.1:9000;
  *           fastcgi_param  SCRIPT_FILENAME  /var/www/zed$fastcgi_script_name;
  *           include        fastcgi_params;
  *       }
  *
  *
  * If you don't want to specify the server domain, you can use get_server_url:
  *      $Config['SiteURL'] = get_server_url() . '/hypership';
  *      $Config['SiteURL'] = get_server_url();
  *
  *
  *
  * !!! No trailing slash !!!
  *
  */
 
 $Config['SiteURL'] = get_server_url();
 $Config['BaseURL'] = '';
 
 //AJAX callbacks URL
 $Config['DoURL'] = $Config['SiteURL'] . "/do.php";
 
 ////////////////////////////////////////////////////////////////////////////////
 ///                                                                          ///
 /// IV. Static content                                                       ///
 ///                                                                          ///
 ////////////////////////////////////////////////////////////////////////////////
 
 //Where the static content is located?
 //Static content = 4 directories: js, css, img and content
 //On default installation, those directories are at site root.
 //To improve site performance, you can use a CDN for that.
 //
 //Recommended setting: $Config['StaticContentURL'] = $Config['SiteURL'];
 //Or if Zed is the site root: $Config['StaticContentURL'] = '';
 //With CoralCDN: $Config['StaticContentURL'] =  . '.nyud.net';
 //
 $Config['StaticContentURL'] = '';
 //$Config['StaticContentURL'] = get_server_url() . '.nyud.net';
 
 //Site content
 define('CONTENT_DIR', Environment::getOr('CONTENT_DIR', 'content'));
 define('CONTENT_USERS_DIR', Environment::getOr(
     'CONTENT_USERS_DIR', CONTENT_DIR . '/users'
 ));
 
 //Scenes
 define('SCENE_DIR', CONTENT_DIR . "/scenes");
 define('SCENE_URL', $Config['StaticContentURL'] . '/content/scenes');
 
 //Stories
 define('STORIES_DIR', CONTENT_DIR . "/stories");
 
 //Profile's photos
 define('PHOTOS_DIR', CONTENT_DIR . "/users/_photos");
 define('PHOTOS_URL', $Config['StaticContentURL'] . '/content/users/_photos');
 
 //ImageMagick paths
 //Be careful on Windows platform convert could match the NTFS convert command.
 $Config['ImageMagick']['convert'] = 'convert';
 $Config['ImageMagick']['mogrify'] = 'mogrify';
 $Config['ImageMagick']['composite'] = 'composite';
 $Config['ImageMagick']['identify'] = 'identify';
 
 ////////////////////////////////////////////////////////////////////////////////
 ///                                                                          ///
 /// V. Caching                                                               ///
 ///                                                                          ///
 ////////////////////////////////////////////////////////////////////////////////
 
 /*
  * Some data (Smarty, OpenID and sessions) are cached in the cache directory.
  *
  * Security tip: you can move this cache directory outside the webserver tree.
  */
 define('CACHE_DIR', Environment::getOr('CACHE_DIR', 'cache'));
 
 /*
  * Furthermore, you can also enable a cache engine, like memcached, to store
  * data from heavy database queries, or frequently accessed stuff.
  *
  * To use memcached:
  *    - $Config['cache']['engine'] = 'memcached';
  *    - $Config['cache']['server'] = 'localhost';
  *    - $Config['cache']['port']   = 11211;
  *
  * To disable cache:
  *    - $Config['cache']['engine'] = 'void';
  *    (or don't write nothing at all)
  */
 $Config['cache']['engine'] = 'void';
 
 ////////////////////////////////////////////////////////////////////////////////
 ///                                                                          ///
 /// VI. Sessions and authentication code                                     ///
 ///                                                                          ///
 ////////////////////////////////////////////////////////////////////////////////
 
 //If you want to use a common table of sessions / user handling
 //with several websites, specify a different resource id for each site.
 $Config['ResourceID'] = 21;
 
 //Enable OpenID authentication
 //$Config['OpenID'] = true;
 
 //Enable YubiKey authentication
 //API 12940
 //For YubiCloud API key - create yours at https://upgrade.yubico.com/getapikey/
 //$Config['YubiCloud']['ClientID']  = 12345;
 //$Config['YubiCloud']['SecretKey'] = 'Base64SecretKeyHere';
 
 //PHP variables
 ini_set('session.save_path', CACHE_DIR . '/sessions');
 
 //4 days, for week-end story pause and continue url
 ini_set('session.gc_maxlifetime', 345600);
 
 ////////////////////////////////////////////////////////////////////////////////
 ///                                                                          ///
 /// VII. Builder                                                             ///
 ///                                                                          ///
 ////////////////////////////////////////////////////////////////////////////////
 
 //Zed can invoke a slightly modified version of HOTGLUE to build zones.
 $Config['builder']['hotglue']['enable'] = true;
 $Config['builder']['hotglue']['URL'] = '/apps/hotglue/index.php';
diff --git a/includes/content/zone.php b/includes/content/zone.php
index c9c0dc3..8986505 100644
--- a/includes/content/zone.php
+++ b/includes/content/zone.php
@@ -1,203 +1,203 @@
 <?php
 
 /**
  * Zone class
  *
  * Zed. The immensity of stars. The HyperShip. The people.
  *
  * (c) 2010, Dereckson, some rights reserved.
  * Released under BSD license.
  *
  * @package     Zed
  * @subpackage  Content
  * @author      Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
  * @copyright   2010 Sébastien Santoro aka Dereckson
  * @license     http://www.opensource.org/licenses/bsd-license.php BSD
  * @version     0.1
  * @link        http://scherzo.dereckson.be/doc/zed
  * @link        http://zed.dereckson.be/
  * @filesource
  */
 
 /**
  * Content zone class
  *
- * A zone is a physical place, independent from the location.
+ * A zone is a physical place, independent of the location.
  * This mechanism allows to more easily move zones.
  *
  * This class maps the content_zones table.
  */
 class ContentZone {
 
     public $id;
     public $title;
     public $type;
     public $params;
     public $deleted = false;
 
     /**
      * Initializes a new instance of a zone object
      *
      * @param int $id The zone ID
      */
     function __construct ($id = '') {
         if ($id) {
             $this->id = $id;
             return $this->load_from_database();
         }
     }
 
     /**
      * Loads the object zone (ie fill the properties) from the $_POST array
      */
     function load_from_form () {
         if (array_key_exists('title', $_POST)) {
             $this->user_id = $_POST['title'];
         }
         if (array_key_exists('type', $_POST)) {
             $this->user_id = $_POST['type'];
         }
         if (array_key_exists('params', $_POST)) {
             $this->user_id = $_POST['params'];
         }
         if (array_key_exists('deleted', $_POST)) {
             $this->user_id = $_POST['deleted'];
         }
     }
 
     /**
      * Loads the object zone (ie fill the properties) from the $row array
      */
     function load_from_row ($row) {
         $this->id = $row['zone_id'];
         $this->title = $row['zone_title'];
         $this->type = $row['zone_type'];
         $this->params = $row['zone_params'];
         $this->deleted = (bool)$row['zone_deleted'];
     }
 
     /**
      * Loads the object zone (ie fill the properties) from the database
      */
     function load_from_database () {
         global $db;
         $id = $db->escape($this->id);
 
         $sql = "SELECT * FROM " . TABLE_CONTENT_ZONES . " WHERE zone_id = '" . $id . "'";
         if (!$result = $db->query($sql)) {
             message_die(SQL_ERROR, 'Unable to query content_zones', '', __LINE__, __FILE__, $sql);
         }
         if (!$row = $db->fetchRow($result)) {
             $this->lastError = 'Zone unknown: ' . $this->id;
             return false;
         }
         $this->load_from_row($row);
         return true;
     }
 
     /**
      * Saves the object to the database
      */
     function save_to_database () {
         global $db;
         $id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
         $title = $db->escape($this->title);
         $type = $db->escape($this->type);
         $params = $db->escape($this->params);
         $deleted = $this->deleted ? 1 : 0;
 
         $sql = "REPLACE INTO " . TABLE_CONTENT_ZONES . " (`zone_id`, `zone_title`, `zone_type`, `zone_params`, `zone_deleted`) VALUES ($id, '$title', '$type', '$params', $deleted)";
         if (!$db->query($sql)) {
             message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
         }
 
         if (!$this->id) {
             $this->id = $db->nextId();
         }
     }
 
     /**
      * Assigns the zone at the specified location.
      *
      * @param string $location_global the global location
      * @param string $location_global the local location
      * @param bool $delete_old_locations if true, delete old locations
      */
     function assign_to ($location_global, $location_local, $delete_old_locations = true) {
         if (!$this->id) {
             $this->save_to_database();
         }
         global $db;
         if ($delete_old_locations) {
             $sql = "DELETE FROM " . TABLE_CONTENT_ZONES_LOCATIONS . " WHERE zone_id = " . $this->id;
             if (!$db->query($sql)) {
                 message_die(SQL_ERROR, "Unable to delete", '', __LINE__, __FILE__, $sql);
             }
         }
         $g = $db->escape($location_global);
         $l = $db->escape($location_local);
         $sql = "REPLACE INTO " . TABLE_CONTENT_ZONES_LOCATIONS . " (location_global, location_local, zone_id) VALUES ('$g', '$l', $this->id)";
         if (!$db->query($sql)) {
             message_die(SQL_ERROR, "Unable to set zone location", '', __LINE__, __FILE__, $sql);
         }
     }
 
     /**
      * Gets the zone at specified location
      *
      * @param string $location_global the global location
      * @param string $location_local the local location
      * @param bool $create if the zone doesn't exist, create it [optional] [default value: false]
      * @return ContentZone the zone, or null if the zone doesn't exist and $create is false
      */
     static function at ($location_global, $location_local, $create = false) {
         global $db;
         $g = $db->escape($location_global);
         $l = $db->escape($location_local ?? "");
         $sql = "SELECT * FROM " . TABLE_CONTENT_ZONES_LOCATIONS . " WHERE location_global = '$g' AND location_local = '$l'";
         if (!$result = $db->query($sql)) {
             message_die(SQL_ERROR, "Unable to set zone location", '', __LINE__, __FILE__, $sql);
         }
         if ($row = $db->fetchRow($result)) {
             return new ContentZone($row['zone_id']);
         } elseif ($create) {
             $zone = new ContentZone();
             $zone->assign_to($location_global, $location_local);
             return $zone;
         } else {
             return null;
         }
     }
 
     /**
      * Gets all the zones matching the specified location queries
      *
      * @param string $location_global_query the global location query
      * @param string $location_local_query the local location query
      * @return Array a ContentZone array, with each item a zone found
      *
      * [SECURITY] They are passed as is in SQL [R]LIKE queries, you can't inject users expression.
      *
      * The following properties are added to the ContentZone items of the returned array:
      *    - location_global
      *    - location_local
      */
     static function search ($location_global_query, $location_local_query, $use_regexp_for_local = false) {
         global $db;
         $zones = [];
 
         $op = $use_regexp_for_local ? 'RLIKE' : 'LIKE';
         $sql = "SELECT * FROM " . TABLE_CONTENT_ZONES_LOCATIONS . " WHERE location_global LIKE '$location_global_query' AND location_local $op '$location_local_query'";
 
         if (!$result = $db->query($sql)) {
             message_die(SQL_ERROR, "Unable to set zone location", '', __LINE__, __FILE__, $sql);
         }
         while ($row = $db->fetchRow($result)) {
             $zone = new ContentZone($row['zone_id']);
             $zone->location_global = $row['location_global'];
             $zone->location_local = $row['location_local'];
             $zones[] = $zone;
         }
         return $zones;
     }
 }
diff --git a/includes/error.php b/includes/error.php
index ee960d6..58c0679 100644
--- a/includes/error.php
+++ b/includes/error.php
@@ -1,197 +1,197 @@
 <?php
 
 /**
  * Error handler
  *
  * Zed. The immensity of stars. The HyperShip. The people.
  *
  * (c) 2010, Dereckson, some rights reserved.
  * Released under BSD license.
  *
  * This error handler uses the same idea and message_die method signature
  * of the phpBB 2 one.
  *
  * @package     Zed
  * @subpackage  Keruald
  * @author      Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
  * @copyright   2010 Sébastien Santoro aka Dereckson
  * @license     http://www.opensource.org/licenses/bsd-license.php BSD
  * @version     0.1
  * @link        http://scherzo.dereckson.be/doc/zed
  * @link        http://zed.dereckson.be/
  * @filesource
  *
  * @todo delete old_message_die method and write alternative HTML textual output
  *       in the message_die method
  */
 
 ///
 /// Error constants
 ///
 
 /**
  * SQL_ERROR is the constant meaning the error is a SQL error.
  *
  * As a message_die function parameter, it allows to add SQL specific debug information.
  */
 define ("SQL_ERROR", 65);
 
 /**
  * HACK_ERROR is the constant meaning access is non authorized to the resource.
  *
  * It encompasses two problematics:
  *     the URL points to a resource belonging to another user or for the current user have no access right (for malformed URL, pick instead GENERAL_ERROR) ;
  *     the user is anonymous, instead to be logged in.
  *
  * A suggested way to handle the second problematic is to store in hidden input
  * fields or better in the session the previous form data, and to print a login
  * form.
  *
- * If you implement this, you don't even need to distinguishes between the two
+ * If you implement this, you don't even need to distinguish between the two
  * cases, as once logged in, the regular HACK_ERROR could also be printed.
  */
 define ("HACK_ERROR", 99);
 
 /**
  * GENERAL_ERROR is the constant meaning the error is general, ie not covered by
  * another more specific error constant.
  */
 define ("GENERAL_ERROR", 117);
 
 ///
 /// Error helper functions
 ///
 
 /**
  * Output a general error, with human-readable information about the specified
  * expression as error message ; terminates the current script.
  *
  * @see message_die
  *
  * @param mixed $expression the expression to be printed
  * @param string $title the message title (optional, default will be 'Debug')
  */
 function dieprint_r ($expression, $title = '')  : never {
     if (!$title) {
     $title = 'Debug'; //if title is omitted or false/null, default title
     }
     message_die(GENERAL_ERROR, '<pre>' . print_r($expression, true) .'</pre>', $title);
 }
 
 /**
  * Outputs an error message and terminates the current script.
  *
  * Error will be output through Smarty one of the following templates :
  *     error_block.tpl if the header have already been printed ;
  *     error.tpl if the error occurred before the header were called and printed.
  *
  * If smarty couldn't be loaded, old_message_die method will be called, which
  * produces a table output.
  *
  * @param int $msg_code an integer constant identifying the error (HACK_ERROR, SQL_ERROR, GENERAL_ERROR)
  * @param string $msg_text the error message text (optional, but recommended)
  * @param string $msg_title the error message title (optional)
  * @param int $err_line the line number of the file where the error occurred (optional, suggested value is __LINE__)
  * @param string $err_line the path of file where the error occurred (optional, suggested value is __FILE__)
  * @param string $sql the SQL query (optional, used only if msg_code is SQL_ERROR)
  */
 function message_die ($msg_code, $msg_text = '', $msg_title = '', $err_line = '', $err_file = '', $sql = '') : never {
     global $smarty, $db;
 
     if ($smarty) {
         $debug_text = $msg_text;
 
         if ($err_line && $err_file) {
             $debug_text .= ' &mdash; ' . $err_file. ', ' . lang_get('line') . ' ' . $err_line ;
         }
 
         switch ($msg_code) {
             case HACK_ERROR:
                 $smarty->assign('TITLE', lang_get('UnauthorizedAccess'));
                 break;
 
             case SQL_ERROR:
                 $smarty->assign('TITLE', lang_get('SQLError'));
                 $sql_error = $db->error();
                 if ($sql_error['message'] != '') {
                     $debug_text .= '<br />' . lang_get('Error') . ' n° ' . $sql_error['code'] . lang_get('_t') .
                                 ' ' .$sql_error['message'];
                 }
                 $debug_text .= "</p><h2>Query:</h2><p>$sql";
                 break;
 
             default:
                 $smarty->assign('WAP', "Message code error.<br />Expected: HACK_ERROR, SQL_ERROR, GENERAL_ERROR");
                 //Falls to GENERAL_ERROR
 
             case GENERAL_ERROR:
                 if ($msg_title) {
             $smarty->assign('TITLE', $msg_title);
         } else {
             $smarty->assign('TITLE', lang_get('GeneralError'));
         }
                 break;
         }
 
 
         $smarty->assign('ERROR_TEXT', $debug_text);
         $template = (defined('HEADER_PRINTED') &&  HEADER_PRINTED) ? "error_block.tpl" : "error.tpl";
     $smarty->display($template);
         exit;
     } else {
         old_message_die($msg_code, $msg_text, $msg_title, $err_line, $err_file, $sql);
     }
 }
 
 /**
  * Outputs an error message and terminates the current script.
  *
  * This is the message_die method from Espace Win, used on Zed as fallback if Smarty isn't initialized yet.
  * Former "german style" error HTML markups have been removed.
  *
  * @param int $msg_code an integer constant identifying the error (HACK_ERROR, SQL_ERROR, GENERAL_ERROR)
  * @param string $msg_text the error message text (optional, but recommended)
  * @param string $msg_title the error message title (optional)
  * @param int $err_line the line number of the file where the error occurred (optional, suggested value is __LINE__)
  * @param string $err_line the path of file where the error occurred (optional, suggested value is __FILE__)
  * @param string $sql the SQL query (optional, used only if msg_code is SQL_ERROR)
  *
  * @deprecated since 0.1
  */
 function old_message_die($msg_code, $msg_text = '', $msg_title = '', $err_line = '', $err_file = '', $sql = '') : never {
     global $db, $Utilisateur;
     $sql_store = $sql;
 
     if ($msg_code == HACK_ERROR && $Utilisateur[user_id] < 1000) {
         die("You must be logged in to access to this resource.");
     } elseif ($msg_code == HACK_ERROR) {
         $title = "You aren't allowed to access this resource.";
         $debug_text = $msg_text;
     } elseif ($msg_code == SQL_ERROR) {
         $title = "SQL error";
         $sql_error = $db->error();
         $debug_text = $msg_text;
         if ($err_line != '' && $err_file != '') {
             $debug_text .= ' in ' . $err_file. ', line ' . $err_line ;
         }
         if ($sql_error['message'] != '') {
             $debug_text .= '<br />Error #' . $sql_error['code'] . ': ' . $sql_error['message'];
         }
         if ($sql_store != '') {
             $debug_text .= "<br /><strong>$sql_store</strong>";
         }
     } elseif ($msg_code == GENERAL_ERROR) {
         $title = $msg_title;
         $debug_text = $msg_text;
         if ($err_line && $err_file) {
             $debug_text .= "<br />$err_file, line $err_line";
         }
     }
 
     echo '<div id="fatal_error"><h2 class="fatal_error_title">';
     echo $title;
     echo '</h2><p class="fatal_error_message">';
     echo $debug_text;
     echo '</p></div';
     die;
 }
diff --git a/includes/geo/location.php b/includes/geo/location.php
index 6cd1dbd..8c9c187 100644
--- a/includes/geo/location.php
+++ b/includes/geo/location.php
@@ -1,446 +1,446 @@
 <?php
 
 /**
  * Geo location class.
  *
  * Zed. The immensity of stars. The HyperShip. The people.
  *
  * (c) 2010, Dereckson, some rights reserved.
  * Released under BSD license.
  *
  * 0.1    2010-01-28 18:52    DcK
  *
  * @package     Zed
  * @subpackage  Geo
  * @author      Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
  * @copyright   2010 Sébastien Santoro aka Dereckson
  * @license     http://www.opensource.org/licenses/bsd-license.php BSD
  * @version     0.1
  * @link        http://scherzo.dereckson.be/doc/zed
  * @link        http://zed.dereckson.be/
  * @filesource
  */
 
 use Hypership\Geo\Point3D;
 
 require_once('body.php');
 require_once('place.php');
 require_once('includes/objects/ship.php');
 
 /**
  * Geo location class
  *
  * This class contains properties to get, set or compare a location and
  * explore the geo classes linked to.
  *
- * It quickly allow to parse through the location classes in templates and
+ * It quickly allows to parse through the location classes in templates and
  * controllers.
  *
  * @todo Initialize $point3D from $body or $ship own locations
  * @todo Improve GeoLocation documentation (especially magic properties)
  */
 class GeoLocation {
     /**
      * An array of strings containing location data.
      *
      * In the current class implementation,
      * the first element is the global location
      * and the second element is the local location.
      *
      * @var Array
      */
     private $data;
 
     /**
      * A body object
      *
      * It contains a GeoBody value when the global location is a body
      * ie if $this->data[0][0] == 'B'
      *
      * Otherwise, its value is null.
      *
      * @var GeoBody
      */
     public $body = null;
 
     /**
      * A place object
      *
      * It contains a GeoPlacevalue when the global location is a place
      * ie if $this->data[0][0] == 'B' && strlen($this->data[0]) == 9
      *
      * Otherwise, its value is null.
      *
      * @var GeoPlace
      */
     public $place = null;
 
     /**
      * A point identified by x, y, z coordinates
      */
     public Point3D|null $point3D = null;
 
     /**
      * A ship object
      *
      * It contains a Ship value when the global location is a ship
      * ie if $this->data[0][0] == 'S'
      *
      * Otherwise, its value is null.
      *
      * @var Ship
      */
     public $ship = null;
 
     /**
      * Initializes a new location instance
      *
      * @param string $global the global location
      * @param string local the locallocation
      *
      * @todo Improve local location handling
      */
     function __construct ($global = null, $local = null) {
         if (!$global) {
             $this->data = [];
         } elseif (preg_match("/[BS][0-9]{5}[0-9]{3}/", $global)) {
             $this->data[0] = $global;
         } elseif (preg_match("/[BS][0-9]{5}/", $global)) {
             $this->data[0] = $global;
         } elseif (preg_match("/^xyz\:/", $global)) {
             $coords = sscanf($global, "xyz: [%d, %d, %d]");
             if (count($coords) == 3) {
                 $this->data[0] = $global;
             } else {
                 throw new Exception("Invalid expression: $global");
             }
         } else {
             global $db;
             $name = $db->escape($global);
             $sql = "SELECT location_code FROM " . TABLE_LOCATIONS . " WHERE location_name LIKE '$name'";
             $code = $db->queryScalar($sql);
             if ($code) {
                 $this->data[0] = $code;
                 return;
             }
             throw new Exception("Invalid expression: $global");
         }
 
         //TODO: handle $local in a better way: from the global location, gets
         //a local location handler. Or a some inheritance, like a class
         //HypershipGeoLocation extending GeoLocation.
         if ($local !== null) {
             $this->data[1] = $local;
         }
 
         $this->load_classes();
     }
 
     /**
      * Gets $place, $body and $ship instances if they're needed
      */
     function load_classes () {
         //No data, no class to load
         if (!count($this->data)) {
             return;
         }
 
         //Loads global classes
         $global = $this->data[0];
         $code = substr($global, 1, 5);
         switch ($global[0]) {
             case 'B':
                 switch (strlen($global)) {
                     case 9:
                         $this->place = GeoPlace::from_code($global);
 
                     case 6:
                         $this->body = new GeoBody($code);
                         break;
                 }
                 break;
 
             case 'S':
                 $this->ship = new Ship($code);
                 break;
 
             case 'x':
                 $coords = sscanf($global, "xyz: [%f, %f, %f]");
                 if (count($coords) == 3) {
                     $this->point3D = new Point3D(...$coords);
                 }
                 break;
         }
     }
 
     /**
      * Magic method called when a unknown property is get.
      *
      * Handles $global, $local, $type, $body_code, $ship_code, $place_code,
      *         $body_kind, $containsGlobalLocation, $containsLocalLocation.
      */
     function __get ($variable) {
         switch ($variable) {
             /* main variables */
 
             case 'global':
                 return $this->data[0];
                 break;
 
             case 'local':
                 return (count($this->data) > 1) ? $this->data[1] : null;
                 break;
 
             /* global location */
 
             case 'type':
                 return $this->data[0][0];
 
             case 'body_code':
                 if ($this->data[0][0] == 'B') {
                     return substr($this->data[0], 1, 5);
                 }
                 return null;
 
             case 'place_code':
                 if ($this->data[0][0] == 'B') {
                     return substr($this->data[0], 6, 3);
                 }
                 return null;
 
             case 'ship_code':
                 if ($this->data[0][0] == 'S') {
                     return substr($this->data[0], 1, 5);
                 }
                 return null;
 
             case 'body_kind':
                 if ($this->data[0][0] == 'B' && $this->body != null) {
                     if ($kind = $this->body->kind()) {
                         return $kind;
                     }
                 } elseif ($this->data[0][0] == 'S') {
                     return 'ship';
                 }
                 return 'place';
 
             case 'containsGlobalLocation':
                 return count($this->data) > 0;
 
             case 'containsLocalLocation':
                 return count($this->data) > 1;
 
             default:
                 throw new Exception("Unknown variable: $variable");
                 break;
         }
     }
 
     /**
      * Checks if the place exists
      *
      * @return bool true if the place exists ; otherwise, false.
      *
      * @todo Handle alias
      */
     function exists () {
         $n = count($this->data);
 
         //If not defined, it doesn't exist
         if ($n == 0) {
             return false;
         }
 
         //Checks global location
         switch ($this->data[0][0]) {
             case 'B':
                 switch (strlen($this->data[0])) {
                     case 9:
                         if (!$place = GeoPlace::from_code($this->data[0])) {
                             return false;
                         }
                         break;
 
                     case 6:
                         $body = new GeoBody(substr($this->data[0], 1));
                         if ($body->lastError) {
                             return false;
                         }
                         break;
 
                     default:
                         message_die(GENERAL_ERROR, "Invalid global location expression size: " . $this->data[0], "GeoLocation exists method", __LINE__, __FILE__);
 
                 }
                 break;
 
             case 'S':
                 $ship = new Ship(substr($this->data[0], 1));
                 if ($body->lastError) {
                     return false;
                 }
                 break;
 
             default:
                 message_die(GENERAL_ERROR, "Invalid global location expression size: " . $this->data[0], "GeoLocation exists method", __LINE__, __FILE__);
                 return false;
         }
 
 
         if ($n > 1) {
             if (!isset($place)) {
                 message_die(GENERAL_ERROR, "Can't check if a local place exists for the following location: " . $this->data[0], "GeoLocation exists method", __LINE__, __FILE__);
             }
             if  (!$place->is_valid_local_location($this->data[1])) {
                 return false;
             }
         }
 
         return true;
     }
 
     /**
      * Checks if the place is equals at the specified expression or place
      *
      * @return bool true if the places are equals ; otherwise, false.
      *
      * @todo Create a better set of rules to define when 2 locations are equal.
      */
     function equals ($expression) {
         //Are global location equals?
 
         //TODO: create a better set of rules to define when 2 locations are equal.
         if (is_a($expression, 'GeoLocation')) {
             if (!$this->equals($expression->data[0])) {
                 return false;
             }
             if (count($expression->data) + count($this->data) > 2) {
                 return $expression->data[1] == $this->data[1];
             }
         }
 
         if ($expression == $this->data[0]) {
             return true;
         }
 
         $n1 = strlen($expression);
         $n2 = strlen($this->data[0]);
 
         if ($n1 > $n2) {
             return substr($expression, 0, $n2) == $this->data[0];
         }
 
         return false;
     }
 
     /**
      * Represents the current location instance as a string
      *
      * @return string a string representing the current location
      */
     function __toString () {
         if (!$this->data[0]) {
             return "";
         }
 
         switch ($this->data[0][0]) {
             case 'S':
                 $ship = new Ship($this->ship_code);
                 $location[] = $ship->name;
                 break;
 
             case 'B':
                 $body = new GeoBody($this->body_code);
                 $location[] = $body->name ?: lang_get('UnknownBody');
 
                 if (strlen($this->data[0]) == 9) {
                     $place = GeoPlace::from_code($this->data[0]);
                     $location[] = $place->name ?: lang_get('UnknownPlace');
                 }
                 break;
 
             case 'x':
                 $pt = $this->point3D->toSpherical();
                 return sprintf("(%d, %d°, %d°)", $pt[0], $pt[1], $pt[2]);
 
             default:
                 message_die(GENERAL_ERROR, "Unknown location identifier: $type.<br />Expected: B or S.");
         }
 
         return implode(", ", array_reverse($location));
     }
 
 
     /**
      * Magic method called when a unknown property is set.
      *
      * Handles $global, $local, $type, $body_code, $ship_code, $place_code
      */
     function __set ($variable, $value) {
         switch ($variable) {
             /* main variables */
 
             case 'global':
                 $this->data[0] = $value;
                 break;
 
             case 'local':
                 $this->data[1] = $value;
                 break;
 
             /* global location */
 
             case 'type':
                 if ($value == 'B' || $value == 'S') {
                     if (!$this->data[0]) {
                         $this->data[0] = $value;
                     } else {
                         $this->data[0][0] = $value;
                     }
                 }
                 break;
 
             case 'body_code':
                 if (preg_match("/[0-9]{1,5}/", $value)) {
                     $value = sprintf("%05d", $value);
                     if (!$this->data[0]) {
                         $this->data[0] = "B" . $value;
                         return;
                     } elseif ($this->data[0][0] == 'B') {
                         $this->data[0] = "B" . $value . substr($this->data[0], 6);
                         return;
                     }
                     throw new Exception("Global location isn't a body.");
                 }
                 throw new Exception("$value isn't a valid body code");
 
             case 'ship_code':
                 if (preg_match("/[0-9]{1,5}/", $value)) {
                     $value = sprintf("%05d", $value);
                     if (!$this->data[0]) {
                         $this->data[0] = "S" . $value;
                         return;
                     } elseif ($this->data[0][0] == 'S') {
                         $this->data[0] = "S" . $value . substr($this->data[0], 6);
                         return;
                     }
                     throw new Exception("Global location isn't a ship.");
                 }
                 throw new Exception("$value isn't a valid ship code");
 
             case 'place_code':
                 if (!preg_match("/[0-9]{1,3}/", $value)) {
                     throw new Exception("$value isn't a valid place code");
                 }
                 $value = sprintf("%03d", $value);
                 if ($this->data[0][0] == 'B') {
                     $this->data[0] = substr($this->data[0], 0, 6) . $value;
                 }
                 throw new Exception("Global location isn't a body.");
 
             default:
                 throw new Exception("Unknown variable: $variable");
                 break;
         }
     }
 }
diff --git a/includes/geo/scene.php b/includes/geo/scene.php
index 2de8687..6d6c881 100644
--- a/includes/geo/scene.php
+++ b/includes/geo/scene.php
@@ -1,212 +1,212 @@
 
 <?php
 
 /**
  * Geo scene class.
  *
  * Zed. The immensity of stars. The HyperShip. The people.
  *
  * (c) 2010, Dereckson, some rights reserved.
  * Released under BSD license.
  *
  * @package     Zed
  * @subpackage  Geo
  * @author      Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
  * @copyright   2010 Sébastien Santoro aka Dereckson
  * @license     http://www.opensource.org/licenses/bsd-license.php BSD
  * @version     0.1
  * @link        http://scherzo.dereckson.be/doc/zed
  * @link        http://zed.dereckson.be/
  * @filesource
  */
 
 require_once('location.php');
 require_once('sceneindex.php');
 
 if (!defined('SCENE_DIR')) {
     /**
      * The directory containing scenes files
      */
     define('SCENE_DIR', 'content/scenes');
 }
 
 /**
  * Geo scene class
  *
  * This class provides methods to determine and renders the local scene.
  */
 class GeoScene {
     /**
      * Last error or warning
      *
      * @var string
      */
     public $lastError = "";
 
     /**
      * File scene to serve
      *
      * @var string
      */
     public $sceneFile;
 
     /**
      * The location of the scene to print
      *
      * @var GeoLocation
      */
     public $location;
 
     /**
      * Initializes a new GeoScene instance
      *
      * @param GeoLocation $location location the scene is to print
      */
     function __construct ($location) {
         $this->location = $location;
 
         //Gets local scene
         if ($location->containsLocalLocation) {
             if ($this->get_local_scene()) {
                 return;
             }
         }
 
         //Gets global scene
         if ($location->containsGlobalLocation) {
             if ($this->get_global_scene()) {
                 return;
             }
         }
 
         //If not scene found, let's set a warning
         $this->lastError = "No scene found.";
     }
 
     /**
      * Gets local scene
      *
      * @return boolean true if a scene have been found ; otherwise, false.
      */
     private function get_local_scene () {
         //From the index
         $index = GeoSceneIndex::Load(SCENE_DIR);
         if ($tpl = $index->get_local_template($this->location->global, $this->location->local)) {
             $this->sceneFile = SCENE_DIR . '/' . $tpl;
             return true;
         }
 
         //From filename
         $expression = $this->location->global . ' ' . $this->location->local;
         if ($this->try_get_scene($expression)) {
             return true;
         }
         return false;
     }
 
     /**
      * Gets global scene
      *
      * @return boolean true if a scene have been found ; otherwise, false.
      */
     private function get_global_scene () {
         $location = $this->location;
         if ($location->place) {
             if ($this->try_get_scene($location->global)) {
                 return true;
             }
         }
         if ($location->body) {
             if ($this->try_get_scene('B' . $location->body->code)) {
                 return true;
             }
         }
         return false;
     }
 
     /**
      * Gets file extension
      *
      * @param string $file the file path
      * @return string the file extension
      */
     public static function get_file_extension (string $file) : string {
         $pathinfo = pathinfo($file);
 
         return $pathinfo['extension'] ?? "";
     }
 
     /**
      * Renders the file
      */
     public function render () {
         if ($file = $this->sceneFile) {
             switch ($ext = GeoScene::get_file_extension($file)) {
                 case 'png':
                 case 'jpg':
                 case 'gif':
                 case 'bmp':
                     echo "<img src=\"$file\" />";
                     break;
 
                 case 'tpl':
                     global $smarty, $Config;
 
                     //$this->location is the object reference
                     //Some objects like the hypership move, so we also need to know where there are.
                     //From the template, this object location is assigned to $location
                     //To get $this->location from template, use $CurrentPerso->location
                     if ($this->location->body) {
                         $smarty->assign("location", new GeoLocation($this->location->body->location));
                     } elseif ($this->location->ship) {
                         $smarty->assign("location", new GeoLocation($this->location->ship->location));
                     }
 
                     //Gets zone information
                     require_once('includes/content/zone.php');
                     if ($zone = ContentZone::at($this->location->global, $this->location->local)) {
                         $smarty->assign('zone', $zone);
                     }
 
                     //Scene-specific variables
                     $smarty->assign("SCENE_URL", defined('SCENE_URL') ? SCENE_URL : '/' . SCENE_DIR);
                     if ($Config['builder']['hotglue']['enable']) {
                          $smarty->assign("HOTGLUE", $Config['builder']['hotglue']['URL']);
             }
                     lang_load('scenes.conf', $this->location->global);
 
                     //Displays scene
                     $smarty->display($file);
                     break;
 
                 case 'php':
                     message_die(HACK_ERROR, ".php scene files not allowed without review", '', __LINE__, __FILE__);
 
                 default:
                     message_die(GENERAL_ERROR, "Can't handle $ext extension for $file scene", 'GeoScene render error', __LINE__, __FILE__);
             }
             echo "\n\n";
         }
     }
 
     /**
      * Tries to get the scene file.
      *
-     * It will tries to find in the scene directory a file with $code as name,
+     * It will try to find in the scene directory a file with $code as name,
      * and .tpl .png .gif .bmp .swf .html or .php as extension.
      *
      * @param string the location code (and filename)
      * @return bool true if a scene file have been found and set ; otherwise, false.
      */
     private function try_get_scene ($code) {
         $file = SCENE_DIR . "/$code";
         $extensions = ['tpl', 'png', 'jpg', 'gif', 'bmp', 'swf', 'html', 'php'];
         foreach ($extensions as $ext) {
             if (file_exists("$file.$ext")) {
                 $this->sceneFile = "$file.$ext";
                 return true;
             }
         }
         return false;
     }
 }
diff --git a/includes/geo/sceneindex.php b/includes/geo/sceneindex.php
index bd8c8f8..1b24e1d 100644
--- a/includes/geo/sceneindex.php
+++ b/includes/geo/sceneindex.php
@@ -1,222 +1,222 @@
 <?php
 
 /**
  * Geo scene index class.
  *
  * Zed. The immensity of stars. The HyperShip. The people.
  *
  * (c) 2010, Dereckson, some rights reserved.
  * Released under BSD license.
  *
  * @package     Zed
  * @subpackage  Geo
  * @author      Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
  * @copyright   2010 Sébastien Santoro aka Dereckson
  * @license     http://www.opensource.org/licenses/bsd-license.php BSD
  * @version     0.1
  * @link        http://scherzo.dereckson.be/doc/zed
  * @link        http://zed.dereckson.be/
  * @filesource
  *
  * This class implements a singleton pattern.
  */
 
 require_once('includes/cache/cache.php');
 
 /**
  * Geo scene class
  *
  * This class provides an index of available scene template files.
  */
 class GeoSceneIndex {
     /**
      * Global location templates array
      *
      * Keys are global location codes.
      * Values the relevant template file.
      *
      * @var Array
      */
     public $global_templates;
 
     /**
      * Local location templates 2D array
      *
      * Keys are global and local location codes.
      * Values the relevant template file.
      *
      * e.g. $local_templates['B00017001']['(10, 50, 8)'] => 'B00017_port.tpl'
      *
      * @var Array
      */
     public $local_templates;
 
     /**
      * Time of the last updated file in the scenes directory
      *
      * @var int
      */
     public $updated;
 
     /**
      * The directory where templates are stored
      *
      * @var string
      */
     public $directory;
 
     /**
      * The current index instance array
      *
      * Keys are scene directories (string)
      * Items are GeoSceneIndex instances
      *
      * @var Array
      */
     static $instance = [];
 
     /**
      * Gets the index instance, initializing it if needed
      *
      * @return index the index instance
      */
     static function load ($directory) {
         //Creates the index object if needed
         if (!array_key_exists($directory, self::$instance)) {
             self::$instance[$directory] = new GeoSceneIndex($directory);
         }
 
         return self::$instance[$directory];
     }
 
     /**
      * Initializes a new GeoSceneIndex instance
      *
      * @param string $directory the scene templates directory
      */
     public function __construct ($directory) {
         $this->directory = $directory;
         if (!$this->get_cached_information() || !$this->is_up_to_date()) {
                 $this->refresh_information();
                 $this->set_cached_information();
         }
     }
 
     /**
      * Caches index data
      */
     public function set_cached_information () {
         $cache = Cache::load();
         $cache->set('zed_sceneindex', serialize($this));
     }
 
     /**
      * Gets index from cache
      */
     public function get_cached_information () {
         $cache = Cache::load();
         $cached_index = $cache->get('zed_sceneindex');
 
         if ($cached_index === false) {
             return false;
         }
 
         $index = unserialize($cached_index);
 
         $this->global_templates = $index->global_templates;
         $this->local_templates = $index->local_templates;
         $this->updated = $index->updated;
         $this->directory = $index->directory;
 
         return true;
     }
 
     /**
      * Reads scene templates and indexes information
      */
     public function refresh_information () {
         $this->global_templates = [];
         $this->local_templates = [];
         $this->updated = filemtime($this->directory);
         if ($handle = opendir($this->directory)) {
             while (false !== ($file = readdir($handle))) {
                  if (GeoScene::get_file_extension($file) == 'tpl') {
                     $template = file_get_contents($this->directory . '/' . $file, false, null, 0, 1024);
                     $location = self::get_template_location($template);
                     if ($location[1] !== null) {
                         $this->local_templates[$location[0]][$location[1]] = $file;
                     } elseif ($location[0] != null) {
                         $this->global_templates[$location[0]] = $file;
                     }
                  }
             }
             closedir($handle);
         }
     }
 
     /**
      * Determines if the information is still up to date
      *
      * @return bool true if the information is up to date ; otherwise, false.
      */
     public function is_up_to_date () {
         return ($this->updated == filemtime($this->directory));
     }
 
     /**
      * Gets template location
      *
      * @return Array an string array of the location (two items; global, local)
      *  At key 0, a string with global location, or NULL if not specified
      *  At key 1, a string with local location, or NULL if not specified
      */
     private static function get_template_location ($template) {
         $location = [null, null];
 
         //Gets global location
         $pos1 = strpos($template, "Global location: ");
         if ($pos1 === false) {
-            throw new Exception("No location in template. Any template file must contain a comment line with the string 'Global location: ' followed by the global location matching the template. It should also contains a line 'Local location: ' when applicable.");
+            throw new Exception("No location in template. Any template file must contain a comment line with the string 'Global location: ' followed by the global location matching the template. It should also contain a line 'Local location: ' when applicable.");
         }
         $pos1 += 17;
         $pos2 = strpos($template, "\n", $pos1);
         $location[0] = trim(substr($template, $pos1, $pos2 - $pos1));
 
         //Gets local location
         $pos1 = strpos($template, "Local location: ");
         if ($pos1 !== false) {
             $pos1 += 16;
             $pos2 = strpos($template, "\n", $pos1);
             $location[1] = trim(substr($template, $pos1, $pos2 - $pos1));
         }
 
         return $location;
     }
 
     /**
      * Gets local template file from index
      *
      * @param string $location_global the global location
      * @param string $location_global the local location
      * @return string the relevant template scene file, or NULL if not existing
      */
     public function get_local_template ($location_global, $location_local) {
         if (isset($this->local_templates[$location_global][$location_local])) {
             return $this->local_templates[$location_global][$location_local];
         }
         return null;
     }
 
     /**
      * Gets global template file from index
      *
      * @param string $location_global the global location
      * @return string the relevant template scene file, or NULL if not existing
      */
     public function get_global_template ($location_global) {
         if (isset($this->global_templates[$location_global])) {
             return $this->global_templates[$location_global];
         }
         return null;
     }
 }
diff --git a/includes/objects/perso.php b/includes/objects/perso.php
index 22de763..3727cde 100644
--- a/includes/objects/perso.php
+++ b/includes/objects/perso.php
@@ -1,612 +1,612 @@
 <?php
 
 /**
  * Perso class
  *
  * Zed. The immensity of stars. The HyperShip. The people.
  *
  * (c) 2010, Dereckson, some rights reserved.
  * Released under BSD license.
  *
  * 0.1    2010-01-27 00:39    Autogenerated by Pluton Scaffolding
  * 0.2    2010-01-29 14:39    Adding flags support
  * 0.3    2010-02-06 17:50    Adding static perso hashtable
  * 0.4    2012-07-04 11:37    Refactoring: moving code from index.php
  *
  * @package     Zed
  * @subpackage  Model
  * @author      Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
  * @copyright   2010, 2012 Sébastien Santoro aka Dereckson
  * @license     http://www.opensource.org/licenses/bsd-license.php BSD
  * @version     0.1
  * @link        http://scherzo.dereckson.be/doc/zed
  * @link        http://zed.dereckson.be/
  * @filesource
  */
 
 require_once("includes/geo/location.php");
 
 /**
  * Perso class
  *
  * This class maps the persos table.
  *
  * The class also provides methods
  *     to move or locate a perso,
  *     to gets and sets perso's flags and notes (tables persos_flags and persos_notes),
  *     to gets user's perso or check if a perso is online,
  *     to handle on select and logout events.
  *
  */
 class Perso {
 
     public $id;
     public $user_id;
     public $name;
     public $nickname;
     public $race;
     public $sex;
     public string $avatar = "";
 
     public $location;
     public $location_global;
     public $location_local;
 
     public $flags;
 
     public string $lastError = "";
 
     public static $hashtable_id = [];
     public static $hashtable_name = [];
 
     /**
      * Initializes a new instance
      *
      * @param mixed $data perso ID or nickname
      */
     function __construct ($data = null) {
         if ($data) {
             if (is_numeric($data)) {
                 $this->id = $data;
             } else {
                 $this->nickname = $data;
             }
 
             if (!$this->load_from_database()) {
                 message_die(GENERAL_ERROR, $this->lastError, "Can't authenticate perso");
             }
         } else {
             $this->generate_id();
         }
     }
 
     /**
      * Initializes a new Perso instance if needed or get already available one.
      *
      * @param mixed $data perso ID or nickname
      * @return Perso the perso instance
      */
     static function get ($data = null) : Perso {
         if ($data) {
             //Checks in the hashtables if we already have loaded this instance
             if (is_numeric($data)) {
                 if (array_key_exists($data, Perso::$hashtable_id)) {
                     return Perso::$hashtable_id[$data];
                 }
             } else {
                 if (array_key_exists($data, Perso::$hashtable_name)) {
                     return Perso::$hashtable_name[$data];
                 }
             }
         }
 
         return new Perso($data);
     }
 
     /**
      * Loads the object Perso (ie fill the properties) from the $_POST array
      */
     function load_from_form () {
         if (array_key_exists('user_id', $_POST)) {
             $this->user_id = $_POST['user_id'];
         }
         if (array_key_exists('name', $_POST)) {
             $this->name = $_POST['name'];
         }
         if (array_key_exists('nickname', $_POST)) {
             $this->nickname = $_POST['nickname'];
         }
         if (array_key_exists('race', $_POST)) {
             $this->race = $_POST['race'];
         }
         if (array_key_exists('sex', $_POST)) {
             $this->sex = $_POST['sex'];
         }
         if (array_key_exists('avatar', $_POST)) {
             $this->avatar = $_POST['avatar'];
         }
         if (array_key_exists('location_global', $_POST)) {
             $this->location_global = $_POST['location_global'];
         }
         if (array_key_exists('location_local', $_POST)) {
             $this->location_local = $_POST['location_local'];
         }
     }
 
     /**
      * Loads the object Perso (ie fill the properties) from the database
      */
     function load_from_database () {
         global $db;
 
         //Gets perso
         $sql = "SELECT * FROM " . TABLE_PERSOS;
         if ($this->id) {
             $id = $db->escape($this->id);
             $sql .= " WHERE perso_id = '" . $id . "'";
         } else {
             $nickname = $db->escape($this->nickname);
             $sql .= " WHERE perso_nickname = '" . $nickname . "'";
         }
         if ( !($result = $db->query($sql)) ) {
             message_die(SQL_ERROR, "Unable to query persos", '', __LINE__, __FILE__, $sql);
         }
         if (!$row = $db->fetchRow($result)) {
             $this->lastError = "Perso unknown: " . $this->id;
             return false;
         }
 
         $this->id = $row['perso_id'];
         $this->user_id = $row['user_id'];
         $this->name = $row['perso_name'];
         $this->nickname = $row['perso_nickname'];
         $this->race = $row['perso_race'];
         $this->sex = $row['perso_sex'];
         $this->avatar = $row['perso_avatar'];
         $this->location_global = $row['location_global'];
         $this->location_local = $row['location_local'];
 
         //Gets flags
         $sql = "SELECT flag_key, flag_value FROM " . TABLE_PERSOS_FLAGS .
                " WHERE perso_id = $this->id";
         if (!$result = $db->query($sql)) {
             message_die(SQL_ERROR, "Can't get flags", '', __LINE__, __FILE__, $sql);
         }
         while ($row = $db->fetchRow($result)) {
             $this->flags[$row["flag_key"]] = $row["flag_value"];
         }
 
         //Gets location
         $this->location = new GeoLocation(
             $this->location_global,
             $this->location_local
         );
 
         //Puts object in hashtables
         Perso::$hashtable_id[$this->id] = $this;
         Perso::$hashtable_name[$this->nickname] = $this;
 
         return true;
     }
 
     /**
      * Saves to database
      */
     function save_to_database () {
         global $db;
 
         $id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
         $user_id = $db->escape($this->user_id);
         $name = $db->escape($this->name);
         $nickname = $db->escape($this->nickname);
         $race = $db->escape($this->race);
         $sex = $db->escape($this->sex);
         $avatar = $db->escape($this->avatar);
         $location_global =  $this->location_global ? "'" . $db->escape($this->location_global) . "'" : 'NULL';
         $location_local = $this->location_local ? "'" .  $db->escape($this->location_local) . "'" : 'NULL';
 
         //Updates or inserts
         $sql = "REPLACE INTO " . TABLE_PERSOS . " (`perso_id`, `user_id`, `perso_name`, `perso_nickname`, `perso_race`, `perso_sex`, `perso_avatar`, `location_global`, `location_local`) VALUES ($id, '$user_id', '$name', '$nickname', '$race', '$sex', '$avatar', $location_global, $location_local)";
         if (!$db->query($sql)) {
             message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
         }
 
         if (!$id) {
             //Gets new record id value
             $this->id = $db->nextId();
         }
     }
 
     /**
      * Updates the specified field in the database record
      *
      * @param string $field The field to save
      */
     function save_field ($field) {
         global $db;
         if (!$this->id) {
             message_die(GENERAL_ERROR, "You're trying to update a perso record not yet saved in the database: $field");
         }
         $id = $db->escape($this->id);
         $value = $db->escape($this->$field);
         $sql = "UPDATE " . TABLE_PERSOS . " SET `$field` = '$value' WHERE perso_id = '$id'";
         if (!$db->query($sql)) {
             message_die(SQL_ERROR, "Unable to save $field field", '', __LINE__, __FILE__, $sql);
         }
     }
 
     /**
      * Gets perso location
      *
      * @return string The location names
      */
     public function where () {
         return $this->location->__toString();
     }
 
     /**
      * Moves the perso to a new location
      *
      * @param string $global the global target location
      * @param string $global the local target location
      */
     public function move_to ($global = null, $local = null) {
 
         //Sets global location
         if ($global != null) {
             $this->location_global = $global;
         }
 
         //Sets local location
         if ($local != null) {
             $this->location_local = $local;
         }
 
         //Updates database record
         if ($global != null && $local != null) {
             global $db;
             $perso_id = $db->escape($this->id);
             $g = $db->escape($this->location_global);
             $l = $db->escape($this->location_local);
             $sql = "UPDATE " . TABLE_PERSOS .
                    " SET location_global = '$g', location_local = '$l'" .
                    " WHERE perso_id = '$perso_id'";
             if (!$db->query($sql)) {
                 message_die(SQL_ERROR, "Can't save new $global $local location.", '', __LINE__, __FILE__, $sql);
             }
         } elseif ($global != null) {
             $this->save_field('location_global');
         } elseif ($local != null) {
             $this->save_field('location_local');
         }
 
         //Updates location member
         $this->location = new GeoLocation(
             $this->location_global,
             $this->location_local
         );
     }
 
     /**
      * Gets the specified flag value
      *
      * @param string $key flag key
      * @param mixed $defaultValue default value if the flag doesn't exist
      * @return mixed the flag value (string) or null if not existing
      */
     public function get_flag ($key, $defaultValue = null) {
         return $this->flag_exists($key) ? $this->flags[$key] : $defaultValue;
     }
 
     /**
      * Determines if the specified flag exists
      *
      * @param string $key the flag key to check
      * @return boolean true if the specified flag exists ; otherwise, false.
      */
     public function flag_exists ($key) {
         return array_key_exists($key, $this->flags);
     }
 
     /**
      * Sets the specified flag
      *
      * @param string $key flag key
      * @param string $value flag value (optional, default value: 1)
      */
     public function set_flag ($key, $value = 1) {
         //Checks if flag isn't already set at this value
         if ($this->flags != null && array_key_exists($key, $this->flags) && $this->flags[$key] === $value) {
             return;
         }
 
         //Saves flag to database
         global $db;
         $id = $db->escape($this->id);
         $key = $db->escape($key);
         $value = $db->escape($value);
         $sql = "REPLACE " . TABLE_PERSOS_FLAGS . " SET perso_id = '$id', flag_key = '$key', flag_value = '$value'";
         if (!$db->query($sql)) {
             message_die(SQL_ERROR, "Can't save flag", '', __LINE__, __FILE__, $sql);
         }
 
         //Sets flag in this perso instance
         $this->flags[$key] = $value;
     }
 
     /**
      * Deletes the specified flag
      *
      * @param string $key flag key
      */
     public function delete_flag ($key) {
         global $db;
         if (!array_key_exists($key, $this->flags)) {
             return;
         }
 
         $id = $db->escape($this->id);
         $key = $db->escape($key);
         $sql = "DELETE FROM " . TABLE_PERSOS_FLAGS  .
                " WHERE flag_key = '$key' AND perso_id = '$id' LIMIT 1";
         if (!$db->query($sql)) {
             message_die(SQL_ERROR, "Can't delete flag", '', __LINE__, __FILE__, $sql);
         }
     }
 
     /**
      * Ensures the current perso have the specified flag or exits.
      *
      *
      * @param string $flag the flag to assert
      * @param int $threshold value the flags must strictly be greater than (optional, the default value is 0)
      *
      * Example:
      * <code>
      * $perso->set_flag('quux.foo', 1);
      * //The perso wants to read quux, which we allow with the flag quux.foo
      * $perso->request_flag('quux.foo'); //will be okay
      *
      * //The perso wants also to write quux, which we all allow if quux.foo = 2
      * //The threshold will so be 1, as 2 > 1
      * $perso->request_flag('quux.foo', 1); //Will exits, with a "You don't have quux.foo permission" message
      * </code>
      */
     public function request_flag ($flag, $threshold = 0) {
         if (!array_key_exists($flag, $this->flags) || $this->flags[$flag] <= $threshold) {
             message_die(HACK_ERROR, "You don't have $flag permission.", "Permissions");
         }
     }
 
     /**
      * Gets the specified note
      *
      * @param string $code the note code
      * @return string the note content
      */
     public function get_note ($code) {
         global $db;
         $id = $db->escape($this->id);
         $code = $db->escape($code);
         $sql = "SELECT note_text FROM " . TABLE_PERSOS_NOTES . " WHERE perso_id = '$id' AND note_code LIKE '$code'";
         return $db->queryScalar($sql);
     }
 
     /**
      * Sets the specified note
      *
      * @param string $code the note code
      * @param string $text the note content
      */
     public function set_note ($code, $text) {
         global $db;
         $id = $db->escape($this->id);
         $code = $db->escape($code);
         $text = $db->escape($text);
         $sql = "REPLACE INTO " . TABLE_PERSOS_NOTES  . " (perso_id, note_code, note_text) VALUES ('$id', '$code', '$text')";
         if (!$db->query($sql)) {
             message_die(SQL_ERROR, "Can't save note", '', __LINE__, __FILE__, $sql);
         }
     }
 
     /**
      * Counts the amount of notes the perso have saved
      *
-     * @return int the amount of notes assigned to the this perso
+     * @return int the amount of notes assigned to the perso
      */
     public function count_notes () {
         global $db;
         $id = $db->escape($this->id);
         $sql = "SELECT COUNT(*) FROM " . TABLE_PERSOS_NOTES . " WHERE perso_id = '$id'";
         return $db->queryScalar($sql);
     }
 
     /*
      * Determines if the specified ID is available
      *
      * @param integer $id The perso ID to check
      * @return boolean true if the specified ID is available ; otherwise, false
      */
     public static function is_available_id ($id) {
         global $db;
 
         $sql = "SELECT COUNT(*) FROM " . TABLE_PERSOS . " WHERE perso_id = $id LOCK IN SHARE MODE";
         if (!$result = $db->query($sql)) {
             message_die(SQL_ERROR, "Can't access users table", '', __LINE__, __FILE__, $sql);
         }
         $row = $db->fetchRow($result);
         return ($row[0] == 0);
     }
 
     /**
      * Generates a unique ID for the current object
      */
     private function generate_id () {
         do {
             $this->id = rand(2001, 5999);
         } while (!Perso::is_available_id($this->id));
     }
 
     /**
      * Checks if the nickname is available
      *
      * @param string $nickname the nickname to check
      */
     public static function is_available_nickname ($nickname) {
         global $db;
         $nickname = $db->escape($nickname);
         $sql = "SELECT COUNT(*) FROM " . TABLE_PERSOS . " WHERE perso_nickname LIKE '$nickname' LOCK IN SHARE MODE;";
         if (!$result = $db->query($sql)) {
             message_die(SQL_ERROR, "Utilisateurs non parsable", '', __LINE__, __FILE__, $sql);
         }
         $row = $db->fetchRow($result);
         return ($row[0] == 0);
     }
 
     /**
      * Counts the perso a user have
      *
      * @param int user_id the user ID
      * @return int the user's perso count
      */
     public static function get_persos_count ($user_id) : int {
         global $db;
         $sql = "SELECT COUNT(*) FROM " . TABLE_PERSOS . " WHERE user_id = $user_id";
         return (int)$db->queryScalar($sql);
 
     }
 
     /**
      * Gets an array with all the perso of the specified user
      *
      * @param int $user_id the user ID
      */
     public static function get_persos (int $user_id) : array {
         global $db;
         $user_id = $db->escape($user_id);
         $sql = "SELECT perso_id FROM " . TABLE_PERSOS . " WHERE user_id = $user_id";
         if (!$result = $db->query($sql)) {
             message_die(SQL_ERROR, "Can't get persos", '', __LINE__, __FILE__, $sql);
         }
 
         $persos = [];
         while ($row = $db->fetchRow($result)) {
             $persos[] = Perso::get($row['perso_id']);
         }
         return $persos;
     }
 
     /**
      * Gets the first perso a user have
      * (typically to be used when get_persos_count returns 1 to autoselect)
      *
      * @param int user_id the user ID
      */
     public static function get_first_perso ($user_id) {
         global $db;
         $sql = "SELECT perso_id FROM " . TABLE_PERSOS ."  WHERE user_id = $user_id LIMIT 1";
         if ($perso_id = $db->queryScalar($sql)) {
             return new Perso($perso_id);
         }
     }
 
     /**
      * Determines whether the perso is online
      *
      * @return bool true if the perso is online ; otherwise, false.
      */
     public function is_online () {
         global $db;
         $id = $db->escape($this->id);
         $sql = "SELECT MAX(online) FROM " . TABLE_SESSIONS ." WHERE perso_id = $id";
         if (!$result = $db->query($sql)) {
             message_die(SQL_ERROR, "Unable to query the table", '', __LINE__, __FILE__, $sql);
         }
         $row = $db->fetchRow($result);
         return ($row[0] == 1);
     }
 
     /**
      * This event method is called when the user selects a new perso
      */
     public function on_select () {
         //Session
         set_info('perso_id', $this->id);
         $this->set_flag("site.lastlogin", $_SERVER['REQUEST_TIME']);
         define("PersoSelected", true);
     }
 
     /**
      * This event method is called when the user logs off its account or perso
      */
     public function on_logout () {
         //Clears perso information in $_SESSION and session table
         set_info('perso_id', null);
         clean_session();
     }
 
     /**
      * This event method is called when the perso is created
      */
     public function on_create () {
         //Notifies host
         $this->notify_inviter();
     }
 
    /**
     * Creates a new perso, from a parameter form
     *
     * @param User $user The user to attach the perso to
     * @param Perso $perso A reference to the created perso (don't initialize it, give it a null value)
     * @param array $errors A reference to the arrays containing errors  (should be an empty array, or the method will always return false)
     * @return boolean true if the perso has ben created ; otherwise, false
     */
     public static function create_perso_from_form (User $user, &$perso, &$errors) {
         $perso = new Perso();
         $perso->load_from_form();
         $perso->user_id = $user->id;
 
         //Validates forms
         if (!$perso->name) {
             $errors[] = lang_get("NoFullnameSpecified");
         }
         if (!$perso->race) {
             $errors[] = lang_get("NoRaceSpecified");
             $perso->race = "being";
         }
         if (!$perso->sex) {
             $errors[] = lang_get("NoSexSpecified");
         }
         if (!$perso->nickname) {
             $errors[] = lang_get("NoNicknameSpecified");
         } elseif (!Perso::is_available_nickname($perso->nickname)) {
             $errors[] = lang_get("UnavailableNickname");
         }
 
        if (count($errors)) {
             return false;
        }
 
        //Creates perso
        $perso->save_to_database();
        $perso->on_create();
        return true;
     }
 
     /**
      * Notifies the person having invited this perso
      */
     public function notify_inviter() {
         require_once('includes/objects/message.php');
         require_once('includes/objects/invite.php');
         $message = new Message();
         $message->from = 0;
         $message->to = invite::who_invited($this->id);
         $message->text =  sprintf(
             lang_get('InvitePersoCreated'),
             $this->name,
             get_server_url() . get_url('who', $this->nickname)
         );
         $message->send();
     }
 }
diff --git a/includes/story/choice.php b/includes/story/choice.php
index cfff352..472d856 100644
--- a/includes/story/choice.php
+++ b/includes/story/choice.php
@@ -1,110 +1,110 @@
 <?php
 
 /**
  * Story choice.
  *
  * Zed. The immensity of stars. The HyperShip. The people.
  *
  * (c) 2010, Dereckson, some rights reserved.
  * Released under BSD license.
  *
  * @package     Zed
  * @subpackage  Story
  * @author      Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
  * @copyright   2010 Sébastien Santoro aka Dereckson
  * @license     http://www.opensource.org/licenses/bsd-license.php BSD
  * @version     0.1
  * @link        http://scherzo.dereckson.be/doc/zed
  * @link        http://zed.dereckson.be/
  * @filesource
  */
 
 /**
  * Story choice class
  *
  * This class is a PHP mapping from the Story XML format's <choice> tag.
  *
  * <code>
  * $xml = "<choice goto=\"city.entry\">Descendre vers le centre ville</choice>";
  * $parser = new SimpleXmlElement($xml);
  * $choice = StoryChoice::from_xml($parser);
  *
  * echo $choice->text; //That will output Descendre vers le centre ville
  * echo $choice->goto; //That will output city.entry
  * echo $choice->guid; //That will output any guid generated for this instance
  *
  * $thesamechoice = StoryChoice::from_xml($parser);
  * echo $thesamechoice->guid; //That will output another guid
  * </code>
  */
 class StoryChoice {
     /**
-     * The section key this choices links to
+     * The section key this choice links to
      *
      * @var string
      */
     public $goto;
 
     /**
      * The choice text
      *
      * @var string
      */
     public $text;
 
     /**
      * The choice GUID
      *
      * It will be automatically generated by the constructor, and so is an
      * ephemeral data for this StoryChoice instance.
      *
      * @see new_guid
      *
      * @var string
      */
     public $guid;
 
     /**
      * Constructor
      */
     function __construct () {
         //The guid allows to build temporary URLs to get to right choice
         $this->guid = new_guid();
     }
 
     /**
      * Gets the story text as a string representation of the class
      *
      * @return string The story text
      */
     function __toString () {
         return $this->text;
     }
 
     /**
      * Initializes a new instance of StoryChoice class from a XML element
      *
      * @param SimpleXMLElement the xml element to parse
      * @return StoryChoice the story choice class
      */
     static function from_xml ($xml) {
         $choice = new StoryChoice();
 
         //Parses attribute
         foreach ($xml->attributes() as $key => $value) {
             switch ($key) {
                 case 'goto':
                     $choice->$key = (string)$value;
                     break;
 
                 default:
                     message_die(GENERAL_ERROR, "Unknown attribute: $key = \"$value\"", "Story error");
             }
         }
 
         //Parses content
         $choice->text = (string)$xml;
 
         return $choice;
     }
 }
diff --git a/includes/travel/place.php b/includes/travel/place.php
index d8b3989..ca67540 100644
--- a/includes/travel/place.php
+++ b/includes/travel/place.php
@@ -1,150 +1,150 @@
 <?php
 
 /**
  * TravelPlace class
  *
  * Zed. The immensity of stars. The HyperShip. The people.
  *
  * (c) 2010, Dereckson, some rights reserved.
  * Released under BSD license.
  *
  * 0.1    2010-07-19 22:10    DcK
  *
  * @package     Zed
  * @subpackage  Travel
  * @author      Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
  * @copyright   2010 Sébastien Santoro aka Dereckson
  * @license     http://www.opensource.org/licenses/bsd-license.php BSD
  * @version     0.1
  * @link        http://scherzo.dereckson.be/doc/zed
  * @link        http://zed.dereckson.be/
  * @filesource
  */
 
 /**
  * TravelPlace class
  *
- * The TravelPlace class is a set of rules determining which moves are valid
+ * The TravelPlace class is a set of rules determining what moves are valid
  * in a specific place.
  *
  * @see GeoPlace
  *
  */
 class TravelPlace {
     /**
      * The place code
      *
      * @var string
      */
     public $code;
 
     /**
      * Determines if any local location move is valid
      *
      * @var bool
      */
     public $freeLocalMove = false;
 
     /**
      * Array of strings, each item another place reachable
      *
      * This matches GlobalTravelTo XML tags.
      *
      * @var Array
      */
     public $globalTravelTo = [];
 
     /**
      * Array of array, containing [location, alias, name] entries
      *
      * This matches LocalMove XML tags.
      *
      * @var Array
      */
     public $localMoves = [];
 
     /**
      * Array of array, containing [expression, global_location, local_location] entries
      *
      * This matches RewriteRule XML tags.
      *
      * @var Array
      */
     public $rewriteRules = [];
 
         /**
      * Initializes a new TravelPlace instance, from the specified XML fragment
      *
      * @param string $xml the XML fragment to parse
      * @return TravelPlace the TravelPlace instance matching the specified XML fragment
      */
     static function from_xml ($xml) {
         $travelPlace = new TravelPlace();
 
         //Reads attributes: <TravelPlace code="B00001001" freeLocalMove="true">
         foreach ($xml->attributes() as $key => $value) {
             switch ($key) {
                 case 'code':
                     $travelPlace->code = (string)$value;
                     break;
 
                 case 'freeLocalMove':
                     $travelPlace->freeLocalMove = (boolean)$value;
                     break;
             }
         }
 
         //<GlobalTravelTo code="B00001002" />
         foreach ($xml->GlobalTravelTo as $globalTravelToXml) {
             foreach ($globalTravelToXml->attributes() as $key => $value) {
                 if ($key == "code") {
                     $travelPlace->globalTravelTo[] = (string)$value;
                 }
             }
         }
 
         //<LocalMove local_location="(0, 0, 0)" alias="C0" name="Core" />
         foreach ($xml->LocalMove as $localMoveXml) {
             $localMove = [null, null, null];
             foreach ($localMoveXml->attributes() as $key => $value) {
                 switch ($key) {
                     case 'local_location':
                         $localMove[0] = (string)$value;
                         break;
 
                     case 'alias':
                         $localMove[1] = (string)$value;
                         break;
 
                     case 'name':
                         $localMove[2] = (string)$value;
                         break;
                 }
             }
             $travelPlace->localMoves[] = $localMove;
         }
 
         //<RewriteRule expression="/^T([1-9][0-9]*)$/" global_location="B00001001" local_location="T$1C1" />
         foreach ($xml->RewriteRule as $rewriteRuleXml) {
             $rewriteRule = [null, null, null];
             foreach ($rewriteRuleXml->attributes() as $key => $value) {
                 switch ($key) {
                     case 'expression':
                         $rewriteRule[0] = (string)$value;
                         break;
 
                     case 'global_location':
                         $rewriteRule[1] = (string)$value;
                         break;
 
                     case 'local_location':
                         $rewriteRule[2] = (string)$value;
                         break;
                 }
             }
             $travelPlace->rewriteRules[] = $rewriteRule;
         }
 
         return $travelPlace;
     }
 }
diff --git a/includes/travel/travel.php b/includes/travel/travel.php
index d86010b..1fbe4e9 100644
--- a/includes/travel/travel.php
+++ b/includes/travel/travel.php
@@ -1,173 +1,173 @@
 <?php
 
 /**
  * Travel helper class
  *
  * Zed. The immensity of stars. The HyperShip. The people.
  *
  * (c) 2010, Dereckson, some rights reserved.
  * Released under BSD license.
  *
  * 0.1    2010-07-18 22:05    DcK
  *
  * @package     Zed
  * @subpackage  Travel
  * @author      Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
  * @copyright   2010 Sébastien Santoro aka Dereckson
  * @license     http://www.opensource.org/licenses/bsd-license.php BSD
  * @version     0.1
  * @link        http://scherzo.dereckson.be/doc/zed
  * @link        http://zed.dereckson.be/
  * @filesource
  */
 
 require_once('place.php');
 
 /**
  * Travel helper class
  *
  * The Travel class reads content/travel.xml to get travel special rules
  *
- * It so be able to provide methods determining if a move is or not valid.
+ * It's so able to provide methods determining if a move is or not valid.
  *
  * This class implements a singleton pattern.
  */
 class Travel {
     /**
      * Array of TravelPlace, each one a custom travel rule
      *
      * This array is indexed by TravelPlace code.
      *
      * @var Array
      */
     public $globalTravelTo;
 
     /**
      * Constructor
      */
     function __construct () {
         //Initializes array
         $this->globalTravelTo = [];
     }
 
     /**
      * Gets and initializes if needed the Travel instance
      *
      * @return Travel the Travel instance
      */
     static function load () {
         require_once('includes/cache/cache.php');
         $cache = Cache::load();
 
         if (!$travel = $cache->get('zed_travel')) {
             //Initializes resource and caches it
             $travel = new Travel();
             $travel->load_xml(CONTENT_DIR . "/travel.xml");
             $cache->set('zed_travel', serialize($travel));
             return $travel;
         }
 
         return unserialize($travel);
     }
 
     /**
      * Loads a travel configuration XML file
      *
      * @param string the path to the travel XML file
      */
     function load_xml ($file) {
         $xml = simplexml_load_file($file);
         foreach ($xml->TravelPlace as $travelPlaceXml) {
             $travelPlace = TravelPlace::from_xml($travelPlaceXml);
             $this->globalTravelTo[$travelPlace->code] = $travelPlace;
         }
     }
 
     /**
      * Tries to parse the specified expression, according the rewrite rules
      * (for example defined by the <RewriteRule> xml tags)
      *
      * @param string $expression the expression to parse
      * @param GeoLocation the location where the perso is
      * @param GeoLocation the location where the perso wants to go
      *
      * @return boolean true if the expression have been parsed ; otherwise, false.
      */
     function try_parse_rewrite_rule ($expression, $from, &$to) {
-        //Relevant write rules depends from the location the perso is ($from)
+        //Relevant write rules depends on the location the perso is ($from)
         if (!array_key_exists($from->global, $this->globalTravelTo)) {
             return false;
         }
         $travelPlace = $this->globalTravelTo[$from->global];
         foreach ($travelPlace->rewriteRules as $rule) {
             //$rule is an array [expression, global_location, local_location]
             $subpatterns = [];
             $result = preg_match($rule[0], $expression, $subpatterns);
             if ($result > 0) {
                 //$subpatterns is an array with:
                 //  - at indice 0, the full matched regexp
                 //  - from 1 to n, the (groups) inside the regexp
                 //We need so to replace $1 by $subpatterns[1] and so on.
                 for ($i = count($subpatterns) - 1 ; $i > 0 ; $i--) {
                     $rule[1] = str_replace('$' . $i, $subpatterns[$i], $rule[1]);
                     $rule[2] = str_replace('$' . $i, $subpatterns[$i], $rule[2]);
                 }
                 $to = new GeoLocation($rule[1], $rule[2]);
                 return true;
             }
         }
         return false;
     }
 
     /**
      * Determines if a perso can travel from $from to $to
      *
      * If an alias have been used for $to local location, set correct location.
      *
      * @param GeoLocation the location where the perso is
      * @param GeoLocation the location where the perso wants to go
      * @return boolean true if the travel move is valid ; otherwise, false.
      *
      * @todo From B00001002, goto C1 doesn't work. Alias seems ignored.
      */
     function can_travel ($from, &$to) {
         if ($from->global != $to->global) {
             //Checks if we can locally from $from to $to place
             if (!array_key_exists($from->global, $this->globalTravelTo)) {
                 return false;
             }
             $travelPlace = $this->globalTravelTo[$from->global];
             if (!in_array($to->global, $travelPlace->globalTravelTo)) {
                 return false;
             }
         }
 
         if ($to->containsLocalLocation) {
             //Determines if we've custom rules about local moves in $to
             if (!array_key_exists($to->global, $this->globalTravelTo)) {
                 return false;
             }
             $travelPlace = $this->globalTravelTo[$to->global];
 
             //Is it's an especially allowed movement?
             foreach ($travelPlace->localMoves as $move) {
                 //move is a [location, alias, name]  array
                 //If any of those 3 parameters matches $to->local, it's okay
                 if (in_array($to->local, $move)) {
                     $to->local = $move[0];
                     return true;
                 }
             }
 
             if ($travelPlace->freeLocalMove) {
                 //We can move freely, perfect
                 return true;
             }
 
             //Local move not allowed
             return false;
         }
 
         return true;
     }
 }