Gettext internationalization and english translation

This commit is contained in:
Miraty 2023-01-21 01:27:52 +01:00
parent 6b1b3547c3
commit 335b826559
83 changed files with 2672 additions and 782 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
/db/niver.db
/locales/*/C/LC_MESSAGES/messages.mo

38
DOCS/translation.md Normal file
View File

@ -0,0 +1,38 @@
# Translation with gettext
## As a developer
Extract messages to be translated from the source files and into a Portable Object Template file:
```
xgettext --from-code=UTF-8 --no-wrap -d messages -p locales/ --from-code=UTF-8 *.php */*.php */*/*.php
mv locales/messages.po locales/messages.pot
```
Merge messages into existing Portable Objects:
```
msgmerge --no-wrap locales/fr/C/LC_MESSAGES/messages.po locales/messages.pot -o locales/fr/C/LC_MESSAGES/messages.po
```
## As a translator
### To start a new translation
```
mkdir -p locales/fr/C/LC_MESSAGES/
msginit -i locales/messages.pot -o locales/fr/C/LC_MESSAGES/messages.po
```
### To translate
Edit `locales/fr/C/LC_MESSAGES/messages.po` using either
* any text editor
* a dedicated translation software like [Poedit](https://poedit.net/), [KDE's Lokalize](https://apps.kde.org/lokalize/) or [GNOME Translation Editor](https://wiki.gnome.org/Apps/Gtranslator).
## As an administrator
To compile Portable Objects into Machine Objects:
```
msgfmt locales/fr/C/LC_MESSAGES/messages.po -o locales/fr/C/LC_MESSAGES/messages.mo
```
Machine Objects files are kept in cache by the Gettext extension, so PHP-FPM needs to be restarted to update translations.

View File

@ -4,6 +4,8 @@ docs_prefix = "/docs/"
; Prefix in URL, if any
prefix = ""
public_domains[] = "niver.test"
service_name = "Niver"
service_emoji = "🪐"
[dns]
knotc_path = "/usr/sbin/knotc"

View File

@ -6,9 +6,9 @@ function output($code, $msg = '', $logs = ['']) {
if ($shortCode === 5)
error_log('Niver internal error: ' . strip_tags($msg) . implode(LF, $logs));
$final_message = match ($shortCode) {
2 => ($msg === '') ? '' : '<p><output><strong>Succès</strong> : <em>' . $msg . '</em></output></p>' . LF,
4 => '<p><output><strong>Erreur utilisataire</strong> : <em>' . $msg . '</em></output></p>' . LF,
5 => '<p><output><strong>Server error</strong>: The server encountered an error: <em>' . $msg . '</em></output></p>' . LF,
2 => ($msg === '') ? '' : '<p><output>' . _('<strong>Success</strong>: ') . '<em>' . $msg . '</em></output></p>' . LF,
4 => '<p><output>' . _('<strong>User error</strong>: ') . '<em>' . $msg . '</em></output></p>' . LF,
5 => '<p><output>' . _('<strong>Server error</strong>: ') . '<em>' . $msg . '</em></output></p>' . LF,
};
displayPage(['final_message' => $final_message]);
}
@ -130,5 +130,5 @@ function getAuthToken() {
function checkAuthToken($salt, $hash) {
$correctProof = substr(hash_hmac('sha256', $salt . $_SESSION['id'], SECRET_KEY), 0, 32);
if (hash_equals($correctProof, $hash) !== true)
output(403, 'Preuve incorrecte');
output(403, _('Wrong proof.'));
}

View File

@ -59,12 +59,12 @@ function checkIpFormat($ip) {
return 'A';
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))
return 'AAAA';
output(403, 'IP address malformed.');
output(403, _('IP address malformed.'));
}
function checkAbsoluteDomainFormat($domain) { // If the domain must end with a dot
if (!filter_var($domain, FILTER_VALIDATE_DOMAIN) OR preg_match('/^([a-z0-9_-]{1,63}\.){2,127}$/D', $domain) !== 1)
output(403, 'Domain malformed.');
output(403, _('Domain malformed.'));
}
function formatEndWithDot($str) {

View File

@ -3,7 +3,7 @@
function checkDomainFormat($domain) {
// If the domain must end without a dot
if (!filter_var($domain, FILTER_VALIDATE_DOMAIN) OR !preg_match('/^([a-z0-9_-]{1,63}\.){1,126}[a-z0-9]{1,63}$/D', $domain))
output(403, 'Domain malformed.');
output(403, _('Domain malformed.'));
}
function formatDomain($domain) {

View File

@ -37,9 +37,9 @@ function nsParseCommonRequirements() {
$values['ttl'] = $_POST['ttl-value'] * $_POST['ttl-multiplier'];
if ($values['ttl'] < MIN_TTL)
output(403, 'Les TTLs inférieurs à ' . MIN_TTL . ' secondes ne sont pas autorisés.');
output(403, sprintf(_('TTLs shorter than %s seconds are forbidden.'), MIN_TTL));
if ($values['ttl'] > MAX_TTL)
output(403, 'Les TTLs supérieurs à ' . MAX_TTL . ' secondes ne sont pas autorisés.');
output(403, sprintf(_('TTLs longer than %s seconds are forbidden.'), MAX_TTL));
return $values;
}
@ -53,8 +53,8 @@ function nsListUserZones() {
function nsCheckZonePossession($zone) {
checkAbsoluteDomainFormat($zone);
if (!in_array($zone, query('select', 'zones', ['username' => $_SESSION['id']], 'zone'), true))
output(403, 'You don\'t own this zone on the nameserver.');
if (!in_array($zone, nsListUserZones(), true))
output(403, 'You don\'t own this zone on the name server.');
}
function nsDeleteZone($zone) {
@ -62,7 +62,7 @@ function nsDeleteZone($zone) {
knotcConfExec(["unset 'zone[$zone]'"]);
// Remove Knot zone file
if(unlink(CONF['ns']['knot_zones_path'] . '/' . $zone . 'zone') !== true)
if (unlink(CONF['ns']['knot_zones_path'] . '/' . $zone . 'zone') !== true)
output(500, 'Failed to remove Knot zone file.');
// Remove Knot related data

View File

@ -10,7 +10,7 @@ function regListUserDomains() {
function regCheckDomainPossession($domain) {
if (in_array($domain, regListUserDomains(), true) !== true)
output(403, 'You don\'t own this domain.');
output(403, 'You don\'t own this domain on the registry.');
}
function regDeleteDomain($domain) {

File diff suppressed because it is too large Load Diff

1094
locales/messages.pot Normal file

File diff suppressed because it is too large Load Diff

158
pages.php
View File

@ -2,176 +2,176 @@
define('PAGES', [
'index' => [
'title' => '<span aria-hidden="true">🪐 </span>Niver',
'title' => '<span aria-hidden="true">' . CONF['common']['service_emoji'] . ' </span>' . CONF['common']['service_name'],
],
'auth' => [
'index' => [
'title' => '<span aria-hidden="true">🔐 </span>Authentification',
'description' => 'Gérer son compte',
'title' => '<span aria-hidden="true">🔐 </span>' . _('Authentication'),
'description' => _('Manage account'),
],
'login' => [
'title' => 'Se connecter',
'description' => 'Démarrer une nouvelle session avec un compte existant',
'title' => _('Log in'),
'description' => _('Start a new navigation session with an existing account'),
'require-login' => false,
],
'logout' => [
'title' => _('Log out'),
'description' => _('End the current session and delete cookies and cache'),
],
'register' => [
'title' => 'Créer un compte',
'description' => 'Créer un nouveau compte Niver',
'title' => _('Create account'),
'description' => _('Create a new account, and log in with it'),
'require-login' => false,
'tokens_instance_cost' => 7200,
],
'unregister' => [
'title' => 'Supprimer son compte',
'description' => 'Effacer toutes les données de son compte',
'title' => _('Delete account'),
'description' => _('Erase all current account\'s data'),
],
'approval' => [
'title' => 'Approuver son compte',
'description' => 'Utiliser une clé d\'approbation pour passer à un compte approuvé.',
'title' => _('Switch to an approved account'),
'description' => _('Switch to an approved account using an approval key'),
],
'password' => [
'title' => 'Changer la clé de passe',
'description' => 'Changer la chaîne de caractères permettant de vous authentifier.',
'title' => _('Change password'),
'description' => _('Change the character string used to authenticate yourself'),
],
'username' => [
'title' => 'Changer l\'identifiant',
'description' => 'Changer la chaîne de caractères permettant d\'identifier votre compte.',
],
'logout' => [
'title' => 'Déconnexion',
'description' => 'Terminer la session et effacer ses cookies',
'title' => _('Change username'),
'description' => _('Change the name used to identify your account when logging in and displayed at the start of every page'),
],
],
'reg' => [
'index' => [
'title' => '<span aria-hidden="true">🏷️ </span>Registre <code>' . CONF['reg']['registry'] . '</code>',
'description' => 'Obtenir et gérer la délégation d\'un sous-domaine de <code>' . CONF['reg']['registry'] . '</code>',
'title' => '<span aria-hidden="true">🏷️ </span>' . sprintf(_('%s registry'), '<code>' . CONF['reg']['registry'] . '</code>'),
'description' => sprintf(_('Register and delegate a %s subdomain'), '<code>' . CONF['reg']['registry'] . '</code>'),
],
'register' => [
'title' => 'Enregistrer un nouveau domaine',
'description' => 'Prendre possession d\'un sous-domaine de <code>' . CONF['reg']['registry'] . '</code>',
'title' => _('Register domain'),
'description' => sprintf(_('Get a %s subdomain'), '<code>' . CONF['reg']['registry'] . '</code>'),
'tokens_account_cost' => 3600,
],
'unregister' => [
'title' => 'Effacer un domaine',
'description' => 'Effacer toutes les données d\'un domaine',
'title' => _('Unregister domain'),
'description' => _('Delete all data related to a domain and make it available to the public again'),
],
'print' => [
'title' => 'Afficher les données',
'description' => 'Afficher les enregistrements relatifs à un domaine',
'title' => _('Display domain records'),
'description' => _('Print every record related to a domain and served by the registry'),
],
'ns' => [
'title' => 'Enregistrements <abbr title="Name Server">NS</abbr>',
'description' => 'Indiquer les serveurs de noms de son sous-domaine de <code>' . CONF['reg']['registry'] . '</code>',
'title' => sprintf(_('%s records'), '<abbr title="Name Server">NS</abbr>'),
'description' => sprintf(_('Indicate the name servers of a %s subdomain'), '<code>' . CONF['reg']['registry'] . '</code>'),
],
'ds' => [
'title' => 'Enregistrements <abbr title="Delegation Signer">DS</abbr>',
'description' => 'Déléguer la confiance <abbr title="Domain Name System Security Extensions">DNSSEC</abbr>',
'title' => sprintf(_('%s records'), '<abbr title="Delegation Signer">DS</abbr>'),
'description' => _('Delegate <abbr title="Domain Name System Security Extensions">DNSSEC</abbr> trust'),
],
'transfer' => [
'title' => 'Recevoir un transfert de domaine',
'description' => 'Transférer un domaine vers ce compte',
'title' => _('Receive a domain transfer'),
'description' => _('Transfer a domain owned by another account to the current account'),
],
'glue' => [
'title' => 'Glue Records',
'description' => 'Avancé : Indiquer l\'IP d\'un serveur de noms dont l\'adresse dépend de la zone qu\'il sert',
'title' => _('Glue records'),
'description' => _('Advanced: store the IP address of a name server whose domain is inside the domain it serves'),
],
],
'ns' => [
'index' => [
'title' => '<span aria-hidden="true">📖 </span>Serveurs de noms',
'description' => 'Héberger et gérer les données liées à un domaine',
'title' => '<span aria-hidden="true">📖 </span>' . _('Name servers'),
'description' => _('Host and manage domain\'s records'),
],
'zone-add' => [
'title' => 'Ajouter une zone',
'description' => 'Pour qu\'elle soit gérée par le serveur de noms de Niver',
'title' => _('Add zone'),
'description' => sprintf(_('The zone will be managed by %s name servers'), CONF['common']['service_name']),
'tokens_account_cost' => 1800,
],
'zone-del' => [
'title' => 'Effacer une zone',
'description' => 'Effacer toutes les données d\'une zone',
'title' => _('Delete zone'),
'description' => _('Erase all zone data'),
],
'print' => [
'title' => 'Afficher les données',
'description' => 'Afficher le contenu de la zone',
'title' => _('Display zone'),
'description' => _('Print zonefile content'),
],
'edit' => [
'title' => 'Modifier une zone',
'description' => 'Éditer un fichier de zone',
'title' => _('Edit zone'),
'description' => _('Change zonefile content'),
'tokens_account_cost' => 300,
],
'ip' => [
'title' => 'Enregistrements A et AAAA',
'description' => 'Indiquer l\'adresse IP d\'un domaine',
'title' => _('AAAA and A records'),
'description' => _('Store domain\'s IP address'),
],
'ns' => [
'title' => 'Enregistrement <abbr title="Name Server">NS</abbr>',
'description' => 'Indiquer le serveur de noms d\'une zone',
'title' => sprintf(_('%s records'), '<abbr title="Name Server">NS</abbr>'),
'description' => _('Store zone\'s name server'),
],
'txt' => [
'title' => 'Enregistrement <abbr title="TeXT">TXT</abbr>',
'description' => 'Associer du texte à un domaine',
'title' => sprintf(_('%s records'), '<abbr title="TeXT">TXT</abbr>'),
'description' => _('Associate text to domain'),
],
'caa' => [
'title' => 'Enregistrement <abbr title="Certification Authority Authorization">CAA</abbr>',
'description' => 'Limiter les autorités de certification autorisées à émettre des certificats',
'title' => sprintf(_('%s records'), '<abbr title="Certification Authority Authorization">CAA</abbr>'),
'description' => _('Limit the certificate authorities allowed to certify the domain'),
],
'srv' => [
'title' => 'Enregistrement <abbr title="SeRVice">SRV</abbr>',
'description' => 'Indiquer l\'adresse d\'un service spécifique',
'title' => sprintf(_('%s records'), '<abbr title="SeRVice">SRV</abbr>'),
'description' => _('Store the location of a domain\'s service'),
],
'mx' => [
'title' => 'Enregistrement <abbr title="Mail eXchanger">MX</abbr>',
'description' => 'Indiquer l\'adresse du serveur recevant les courriels',
'title' => sprintf(_('%s records'), '<abbr title="Mail eXchanger">MX</abbr>'),
'description' => _('Store the email server\'s address'),
],
'sshfp' => [
'title' => 'Enregistrement <abbr title="Secure SHell FingerPrint">SSHFP</abbr>',
'description' => 'Indiquer les empreintes des clés <abbr title="Secure SHell">SSH</abbr>',
'title' => sprintf(_('%s records'), '<abbr title="Secure SHell FingerPrint">SSHFP</abbr>'),
'description' => _('Store <abbr title="Secure SHell">SSH</abbr> public keys fingerprints'),
],
'tlsa' => [
'title' => 'Enregistrement <abbr title="Transport Layer Security Association">TLSA</abbr>',
'description' => 'Mettre en place <abbr title="DNS-based Authentication of Named Entities">DANE</abbr> en indiquant l\'empreinte d\'un certificat <abbr title="Transport Layer Security">TLS</abbr>',
'title' => sprintf(_('%s records'), '<abbr title="Transport Layer Security Association">TLSA</abbr>'),
'description' => _('Setup <abbr title="DNS-based Authentication of Named Entities">DANE</abbr> by publishing the <abbr title="Transport Layer Security">TLS</abbr> certificate fingerprint'),
],
'cname' => [
'title' => 'Enregistrement <abbr title="Canonical NAME">CNAME</abbr>',
'description' => 'Définir un domaine comme étant l\'alias d\'un autre',
'title' => sprintf(_('%s records'), '<abbr title="Canonical NAME">CNAME</abbr>'),
'description' => _('Define a domain as an alias of another'),
],
'dname' => [
'title' => 'Enregistrement <abbr title="Delegation NAME">DNAME</abbr>',
'description' => 'Définir les sous-domaines d\'un domaine comme étant les alias des sous-domaines d\'un autre domaine',
'title' => sprintf(_('%s records'), '<abbr title="Delegation NAME">DNAME</abbr>'),
'description' => _('Define all subdomains of a domain as aliases of subdomains of another domain'),
],
'loc' => [
'title' => 'Enregistrement <abbr title="LOCation">LOC</abbr>',
'description' => 'Indiquer des coordonnées géographiques',
'title' => sprintf(_('%s records'), '<abbr title="LOCation">LOC</abbr>'),
'description' => _('Store geographic coordinates'),
],
],
'ht' => [
'index' => [
'title' => '<span aria-hidden="true">🕸️ </span>Hypertexte',
'description' => 'Mettre en ligne son site statique sur un espace <abbr title="SSH File Transfert Protocol">SFTP</abbr>, et le faire répondre en <abbr title="HyperText Transfert Protocol">HTTP</abbr> par DNS ou Tor',
'title' => '<span aria-hidden="true">🕸️ </span>' . _('Web'),
'description' => _('Upload a static website into an <abbr title="SSH File Transfer Protocol">SFTP</abbr> space'),
],
'add-subpath' => [
'title' => 'Accès par sous-chemin <code>' . CONF['ht']['subpath_domain'] . '/</code>',
'description' => 'Son URL ressemblera à <code>https://' . CONF['ht']['subpath_domain'] . '/monsite/</code>',
'title' => sprintf(_('%s subpath access'), '<code>' . CONF['ht']['subpath_domain'] . '/</code>'),
'description' => sprintf(_('Its URL will look like %s'), '<code>https://' . CONF['ht']['subpath_domain'] . '/<em>' . _('mysite') . '</em>/</code>'),
'tokens_account_cost' => 900,
],
'add-subdomain' => [
'title' => 'Accès par sous-domaine de <code>.' . CONF['ht']['subdomain_domain'] . '</code>',
'description' => 'Son URL ressemblera à <code>https://monsite.' . CONF['ht']['subpath_domain'] . '/</code>',
'title' => sprintf(_('%s subdomain access'), '<code>.' . CONF['ht']['subdomain_domain'] . '</code>'),
'description' => sprintf(_('Its URL will look like %s'), '<code>https://<em>' . _('mysite') . '</em>.' . CONF['ht']['subpath_domain'] . '/</code>'),
'tokens_account_cost' => 1800,
],
'add-dns' => [
'title' => 'Accès par domaine dédié et certificat Let\'s Encrypt',
'description' => 'Son URL ressemblera à <code>https://monsite.example/</code>',
'title' => _('Dedicated domain with Let\'s Encrypt certificate access'),
'description' => sprintf(_('Its URL will look like %s'), '<code>https://<em>' . _('mysite') . '</em>.' . PLACEHOLDER_DOMAIN . '/</code>'),
'tokens_account_cost' => 3600,
],
'add-onion' => [
'title' => 'Accès par service Onion',
'description' => 'Son URL ressemblera à <code>http://nrdselxjgryq5fwek2xh3pxg4b26z26eyzlbs4y5lownk465jhaamayd.onion/</code>, qui ne fonctionnera que par le réseau Tor',
'title' => _('Onion service access'),
'description' => sprintf(_('Its URL will look like %s, and work only through the Tor network'), '<code>http://nrdselxjgryq5fwek2xh3pxg4b26z26eyzlbs4y5lownk465jhaamayd.onion/</code>'),
'tokens_account_cost' => 1800,
],
'del' => [
'title' => 'Retirer un accès',
'description' => 'Retirer un accès HTTP déjà existant d\'un sous-dossier de l\'espace SFTP',
'title' => _('Delete access'),
'description' => _('Delete an existing HTTP access from a subdirectory of the SFTP space'),
],
],
]);

View File

@ -1,10 +1,10 @@
<?php
if ($_SESSION['type'] !== 'testing')
output(403, 'Approbation impossible : votre compte est déjà approuvé.');
output(403, _('This account is already approved.'));
if (isset(query('select', 'approval-keys', ['key' => $_POST['key']], 'key')[0]) !== true)
output(403, 'Approbation impossible : cette clé d\'approbation n\'est pas disponible. Elle a été mal saisie, a expiré ou a déjà été utilisée pour un autre compte.');
output(403, _('This approval key is not available. It has been mistyped, used for another account, or has expired.');
query('delete', 'approval-keys', ['key' => $_POST['key']]);
@ -15,4 +15,4 @@ $_SESSION['type'] = 'approved';
insert('approval-keys', ['key' => bin2hex(random_bytes(16))]);
output(200, 'Compte approuvé.');
output(200, _('Account approved.'));

View File

@ -7,12 +7,12 @@ checkUsernameFormat($_POST['username']);
$username = hashUsername($_POST['username']);
if (usernameExists($username) !== true)
output(403, 'Connexion impossible : ce compte n\'existe pas.');
output(403, _('This account does not exist.'));
$id = query('select', 'users', ['username' => $username], 'id')[0];
if (checkPassword($id, $_POST['password']) !== true)
output(403, 'Connexion impossible : clé de passe invalide.');
output(403, _('Wrong password.'));
if (outdatedPasswordHash($id))
changePassword($id, $_POST['password']);

View File

@ -3,8 +3,8 @@
checkPasswordFormat($_POST['new-password']);
if (checkPassword($_SESSION['id'], $_POST['current-password']) !== true)
output(403, 'Changement impossible : clé de passe invalide.');
output(403, _('Wrong current password.'));
changePassword($_SESSION['id'], $_POST['new-password']);
output(200, 'Clé de passe changée.');
output(200, _('Password updated.'));

View File

@ -7,7 +7,7 @@ checkUsernameFormat($_POST['username']);
$username = hashUsername($_POST['username']);
if (usernameExists($username) !== false)
output(403, 'Ce nom de compte est déjà utilisé.');
output(403, _('This username is already taken.'));
rateLimit();

View File

@ -1,7 +1,7 @@
<?php
if (!isset($_POST['delete']))
output(403, 'Il faut confirmer la suppression du compte');
output(403, _('Account deletion must be confirmed.'));
foreach (query('select', 'registry', ['username' => $_SESSION['id']], 'domain') as $domain)
regDeleteDomain($domain);
@ -26,4 +26,4 @@ query('delete', 'users', ['id' => $_SESSION['id']]);
logout();
output(200, 'Compte supprimé.');
output(200, _('Account deleted.'));

View File

@ -5,7 +5,7 @@ checkUsernameFormat($_POST['new-username']);
$username = hashUsername($_POST['new-username']);
if (usernameExists($username) !== false)
output(403, 'Ce nom de compte est déjà utilisé.');
output(403, _('This username is already taken.'));
DB->prepare('UPDATE users SET username = :username WHERE id = :id')
->execute([':username' => $username, ':id' => $_SESSION['id']]);
@ -14,4 +14,4 @@ setupDisplayUsername($_POST['new-username']);
redir('auth/username');
output(200, 'Identifiant changé.');
output(200, _('Username updated.'));

View File

@ -6,25 +6,25 @@ if (dirsStatuses('dns')[$_POST['dir']] !== false)
output(403, 'Wrong value for <code>dir</code>.');
if (query('select', 'sites', ['domain' => $_POST['domain']], 'domain') !== [])
output(403, 'Ce domaine existe déjà sur ce service.');
output(403, _('This domain already exists on this service. Use another one.');
$remoteAaaaRecords = dns_get_record($_POST['domain'], DNS_AAAA);
if (is_array($remoteAaaaRecords) !== true)
output(500, 'Erreur lors de la récupération de l\'enregistrement AAAA.');
output(500, sprintf(_('Can\'t retrieve the %s record.'), 'AAAA');
if (equalArrays([CONF['ht']['ipv6_address']], array_column($remoteAaaaRecords, 'ipv6')) !== true)
output(403, 'Ce domaine doit avoir pour unique enregistrement AAAA <code>' . CONF['ht']['ipv6_address'] . '</code>.');
output(403, sprintf(_('This domain must have %2$s as its only %1$s record.'), 'AAAA', '<code>' . CONF['ht']['ipv6_address'] . '</code>'));
$remoteARecords = dns_get_record($_POST['domain'], DNS_A);
if (is_array($remoteARecords) !== true)
output(500, 'Erreur lors de la récupération de l\'enregistrement A.');
output(500, sprintf(_('Can\'t retrieve the %s record.'), 'A');
if (equalArrays([CONF['ht']['ipv4_address']], array_column($remoteARecords, 'ip')) !== true)
output(403, 'Ce domaine doit avoir pour unique enregistrement A <code>' . CONF['ht']['ipv4_address'] . '</code>.');
output(403, sprintf(_('This domain must have %2$s as its only %1$s record.'), 'A', '<code>' . CONF['ht']['ipv4_address'] . '</code>'));
$remoteTXTRecords = dns_get_record($_POST['domain'], DNS_TXT);
if (is_array($remoteTXTRecords) !== true)
output(500, 'Erreur lors de la récupération de l\'enregistrement TXT.');
output(500, sprintf(_('Can\'t retrieve the %s record.'), 'TXT');
if (preg_match('/^' . preg_quote(SERVER_NAME, '/') . '_domain-verification=([0-9a-f]{8})-([0-9a-f]{32})$/Dm', implode(LF, array_column($remoteTXTRecords, 'txt')), $matches) !== 1)
output(403, 'Aucun enregistrement TXT au format correct trouvé.');
output(403, _('No TXT record with the expected format has been found.'));
checkAuthToken($matches[1], $matches[2]);
@ -56,4 +56,4 @@ exec(CONF['ht']['sudo_path'] . ' ' . CONF['ht']['nginx_reload_cmd'], result_code
if ($code !== 0)
output(500, 'Failed to reload Nginx.');
output(200, 'Accès HTTP par domaine dédié ajouté sur ce dossier !');
output(200, sprintf(_('%s added on this directory.'), PAGE_METADATA['title']);

View File

@ -44,4 +44,4 @@ if ($code !== 0)
output(500, 'Failed to reload Nginx.');
// Tell the user their site address
output(200, 'L\'adresse de votre service Onion HTTP est : <a href="http://' . $onion . '/"><code>http://' . $onion . '/</code></a>');
output(200, sprintf(_('%s added on this directory.'), PAGE_METADATA['title'] . sprintf(_('Its address is: %s'), '<a href="http://' . $onion . '/"><code>http://' . $onion . '/</code></a>');

View File

@ -4,10 +4,10 @@ if (dirsStatuses('subdomain')[$_POST['dir']] !== false)
output(403, 'Wrong value for <code>dir</code>.');
if (preg_match('/^[a-z0-9]{1,32}$/D', $_POST['subdomain']) !== 1)
output(403, 'Label de domaine invalide.');
output(403, _('Invalid domain label.'));
if (query('select', 'sites', ['address' => $_POST['subdomain'], 'type' => 'subdomain']) !== [])
output(403, 'Ce domaine est déjà utilisé sur ce service. Utilisez-en un autre.');
output(403, _('This domain already exists on this service. Use another one.'));
rateLimit();
@ -16,4 +16,4 @@ addSite($_SESSION['id'], $_POST['dir'], $_POST['subdomain'], 'subdomain');
if (symlink(CONF['ht']['ht_path'] . '/' . $_SESSION['id'] . '/' . $_POST['dir'], CONF['ht']['subdomain_path'] . '/' . $_POST['subdomain']) !== true)
output(500, 'Unable to create symlink.');
output(200, 'Accès HTTP par sous-chemin ajouté sur ce dossier !');
output(200, sprintf(_('%s added on this directory.'), PAGE_METADATA['title']));

View File

@ -4,10 +4,10 @@ if (dirsStatuses('subpath')[$_POST['dir']] !== false)
output(403, 'Wrong value for <code>dir</code>.');
if (preg_match('/^[a-z0-9]{1,32}$/D', $_POST['path']) !== 1)
output(403, 'Chemin invalide.');
output(403, _('Invalid path.'));
if (query('select', 'sites', ['address' => $_POST['path'], 'type' => 'subpath']) !== [])
output(403, 'Ce chemin est déjà utilisé sur ce service. Utilisez-en un autre.');
output(403, _('This path already exists on this service. Use another one.'));
rateLimit();
@ -16,4 +16,4 @@ addSite($_SESSION['id'], $_POST['dir'], $_POST['path'], 'subpath');
if (symlink(CONF['ht']['ht_path'] . '/' . $_SESSION['id'] . '/' . $_POST['dir'], CONF['ht']['subpath_path'] . '/' . $_POST['path']) !== true)
output(500, 'Unable to create symlink.');
output(200, 'Accès HTTP par sous-chemin ajouté sur ce dossier !');
output(200, sprintf(_('%s added on this directory.'), PAGE_METADATA['title']));

View File

@ -8,4 +8,4 @@ if (isset(query('select', 'sites', ['username' => $_SESSION['id'], 'address' =>
htDeleteSite($site['address'], $site['type']);
output(200, 'Accès retiré.');
output(200, _('Access removed.'));

View File

@ -20,4 +20,4 @@ knotcZoneExec($_POST['zone'], [
$_POST['value']
]);
output(200, 'Enregistrement ajouté/retiré.');
output(200, _('Modification done.'));

View File

@ -11,4 +11,4 @@ knotcZoneExec($_POST['zone'], [
$_POST['cname']
]);
output(200, 'Enregistrement ajouté/retiré.');
output(200, _('Modification done.'));

View File

@ -11,4 +11,4 @@ knotcZoneExec($_POST['zone'], [
$_POST['dname']
]);
output(200, 'Enregistrement ajouté/retiré.');
output(200, _('Modification done.'));

View File

@ -14,17 +14,17 @@ if (isset($_POST['zone-content'])) { // Update zone
// Generate new zone content
$new_zone_content = $matches['soa'] . LF;
if (strlen($_POST['zone-content']) > ZONE_MAX_CHARACTERS)
output(403, 'La zone n\'est pas autorisée à dépasser ' . ZONE_MAX_CHARACTERS . ' caractères.');
output(403, sprintf(_('The zone is limited to %s characters.'), ZONE_MAX_CHARACTERS));
foreach (explode("\r\n", $_POST['zone-content']) as $line) {
if ($line === '') continue;
if (preg_match('/^(?<domain>[a-z0-9@._-]+)(?:[\t ]+(?<ttl>[0-9]{1,16}))?(?:[\t ]+IN)?[\t ]+(?<type>[A-Z]{1,16})[\t ]+(?<value>.+)$/D', $line, $matches) !== 1)
output(403, 'La zone est mal formatée (selon Niver).');
output(403, _('The following line does not match the expected format: ') . '<code>' . htmlspecialchars($line) . '</code>');
if (in_array($matches['type'], ALLOWED_TYPES, true) !== true)
output(403, 'Le type <code>' . $matches['type'] . '</code> n\'est pas autorisé.');
output(403, sprintf(_('The %s type is not allowed.'), '<code>' . $matches['type'] . '</code>'));
if ($matches['ttl'] !== '' AND $matches['ttl'] < MIN_TTL)
output(403, 'Les TTLs inférieurs à ' . MIN_TTL . ' secondes ne sont pas autorisés.');
output(403, sprintf(_('TTLs shorter than %s seconds are forbidden.'), MIN_TTL));
if ($matches['ttl'] !== '' AND $matches['ttl'] > MAX_TTL)
output(403, 'Les TTLs supérieurs à ' . MAX_TTL . ' secondes ne sont pas autorisés.');
output(403, sprintf(_('TTLs longer than %s seconds are forbidden.'), MAX_TTL));
$new_zone_content .= $matches['domain'] . ' ' . (($matches['ttl'] === '') ? DEFAULT_TTL : $matches['ttl']) . ' ' . $matches['type'] . ' ' . $matches['value'] . LF;
}
@ -35,7 +35,7 @@ if (isset($_POST['zone-content'])) { // Update zone
fwrite($pipes[0], $new_zone_content);
fclose($pipes[0]);
if (proc_close($process) !== 0)
output(403, 'Le contenu de zone envoyé n\'est pas valide (selon <code>kzonecheck</code>).');
output(403, _('Sent zone content is not correct (according to <code>kzonecheck</code>).'));
ratelimit();

View File

@ -11,4 +11,4 @@ knotcZoneExec($_POST['zone'], [
$_POST['ip']
]);
output(200, 'Enregistrement ajouté/retiré.');
output(200, _('Modification done.'));

View File

@ -67,4 +67,4 @@ knotcZoneExec($_POST['zone'], [
$_POST['vp'] . 'm',
]);
output(200, 'Enregistrement ajouté/retiré.');
output(200, _('Modification done.'));

View File

@ -15,4 +15,4 @@ knotcZoneExec($_POST['zone'], [
$_POST['host']
]);
output(200, 'Enregistrement ajouté/retiré.');
output(200, _('Modification done.'));

View File

@ -11,4 +11,4 @@ knotcZoneExec($_POST['zone'], [
$_POST['ns']
]);
output(200, 'Enregistrement ajouté/retiré.');
output(200, _('Modification done.'));

View File

@ -23,4 +23,4 @@ knotcZoneExec($_POST['zone'], [
$_POST['target']
]);
output(200, 'Enregistrement ajouté/retiré.');
output(200, _('Modification done.'));

View File

@ -20,4 +20,4 @@ knotcZoneExec($_POST['zone'], [
$_POST['fp']
]);
output(200, 'Enregistrement ajouté/retiré.');
output(200, _('Modification done.'));

View File

@ -24,4 +24,4 @@ knotcZoneExec($_POST['zone'], [
$_POST['content']
]);
output(200, 'Enregistrement ajouté/retiré.');
output(200, _('Modification done.'));

View File

@ -12,4 +12,4 @@ knotcZoneExec($_POST['zone'], [
'"' . $_POST['txt'] . '"'
]);
output(200, 'Enregistrement ajouté/retiré.');
output(200, _('Modification done.'));

View File

@ -3,17 +3,17 @@
$_POST['domain'] = formatAbsoluteDomain($_POST['domain']);
if (query('select', 'zones', ['zone' => $_POST['domain']], 'zone') !== [])
output(403, 'Cette zone existe déjà sur ce service.');
output(403, _('This zone already exists on the service.'));
exec(CONF['dns']['kdig_path'] . ' ' . ltrim(strstr($_POST['domain'], '.'), '.') . ' NS +short', $parentAuthoritatives);
if ($parentAuthoritatives === [])
output(403, 'Serveurs de noms de la zone parente introuvables');
output(403, _('Parent zone\'s name servers not found.'));
foreach ($parentAuthoritatives as $parentAuthoritative)
checkAbsoluteDomainFormat($parentAuthoritative);
exec(CONF['dns']['kdig_path'] . ' ' . $_POST['domain'] . ' NS @' . $parentAuthoritatives[0] . ' +noidn', $results);
if (preg_match('/^' . preg_quote($_POST['domain'], '/') . '[\t ]+[0-9]{1,8}[\t ]+IN[\t ]+NS[\t ]+(?<salt>[0-9a-f]{8})-(?<hash>[0-9a-f]{32})\._domain-verification\.' . preg_quote(SERVER_NAME, '/') . '\.$/Dm', implode(LF, $results), $matches) !== 1)
output(403, 'Enregistrement d\'authentification introuvable');
output(403, _('NS authentication record not found.'));
checkAuthToken($matches['salt'], $matches['hash']);
@ -49,4 +49,4 @@ knotcConfExec([
"set 'zone[" . $_POST['domain'] . "].template' 'niver'",
]);
output(200, 'La zone a été créée.');
output(200, _('Zone created.'));

View File

@ -4,4 +4,4 @@ nsCheckZonePossession($_POST['zone']);
nsDeleteZone($_POST['zone']);
output(200, 'La zone a été supprimée.');
output(200, _('Zone deleted.'));

View File

@ -27,4 +27,4 @@ knotcZoneExec(CONF['reg']['registry'], [
$_POST['key']
]);
output(200, 'Enregistrement ajouté/retiré.');
output(200, _('Modification done.'));

View File

@ -13,4 +13,4 @@ knotcZoneExec(CONF['reg']['registry'], [
$_POST['ip']
]);
output(200, 'Glue ajouté/retiré.');
output(200, _('Modification done.'));

View File

@ -10,4 +10,4 @@ knotcZoneExec(CONF['reg']['registry'], [
$_POST['ns']
]);
output(200, 'Enregistrement ajouté/retiré.');
output(200, _('Modification done.'));

View File

@ -1,15 +1,15 @@
<?php
if (preg_match('/' . SUBDOMAIN_REGEX . '/D', $_POST['subdomain']) !== 1)
output(403, 'Le nom de domaine doit être composé uniquement d\'entre 4 et 63 lettres minuscules ou chiffre (a-z et 0-9)');
output(403, _('This format of subdomain is not allowed.'));
$domain = formatAbsoluteDomain($_POST['subdomain'] . '.' . CONF['reg']['registry']);
if (query('select', 'registry', ['domain' => $domain], 'domain') !== [])
output(403, 'Ce domaine n\'est pas disponible à l\'enregistrement. Il est déjà enregistré.');
output(403, _('This domain is already registered.'));
if (in_array($_POST['subdomain'], explode(LF, file_get_contents(CONF['common']['root_path'] . '/pg-act/reg/reserved.txt'))))
output(403, 'Ce domaine n\'est pas disponible à l\'enregistrement. Il est réservé.');
output(403, _('This domain is reserved.'));
rateLimit();
@ -19,4 +19,4 @@ insert('registry', [
'last_renewal' => date('Y-m-d H:i:s'),
]);
output(200, 'Domaine ajouté au registre.');
output(200, _('Domain registered.'));

View File

@ -1,16 +1,16 @@
<?php
if (preg_match('/' . SUBDOMAIN_REGEX . '/D', $_POST['domain']) !== 1)
output(403, 'Le nom de domaine semble incorrect');
output(403, 'Wrong domain name format.');
$domain = $_POST['domain'] . '.' . CONF['reg']['registry'];
if (query('select', 'registry', ['username' => $_SESSION['id'], 'domain' => $domain], 'domain') !== [])
output(403, 'Le compte présent possède déjà ce domaine.');
output(403, _('The current account already owns this domain.'));
exec(CONF['dns']['kdig_path'] . ' ' . $domain . ' NS @' . CONF['reg']['address'] . ' +noidn', $results);
if (preg_match('/^' . preg_quote($domain, '/') . '[\t ]+[0-9]{1,8}[\t ]+IN[\t ]+NS[\t ]+(?<salt>[0-9a-f]{8})-(?<hash>[0-9a-f]{32})\._transfer-verification\.' . preg_quote(SERVER_NAME, '/') . '\.$/Dm', implode(LF, $results), $matches) !== 1)
output(403, 'Enregistrement d\'authentification introuvable');
output(403, _('NS authentication record not found.'));
checkAuthToken($matches['salt'], $matches['hash']);
@ -23,4 +23,4 @@ knotcZoneExec(CONF['reg']['registry'], [
$matches['salt'] . '-' . $matches['hash'] . '._transfer-verification.' . SERVER_NAME . '.'
], 'delete');
output(200, 'Le domaine a été transféré vers le compte présent, l\'enregistrement d\'authentification a été automatiquement retiré.');
output(200, _('The domain has been transferred to the current account ; the NS authentication record has been automatically deleted.'));

View File

@ -4,4 +4,4 @@ regCheckDomainPossession($_POST['domain']);
regDeleteDomain($_POST['domain']);
output(200, 'Domaine effacé du registre.');
output(200, _('Domain unregistered.'));

View File

@ -1,10 +1,10 @@
<p>
Ce formulaire permet d'utiliser une clé d'approbation pour valider son compte. Une clé d'approbation est distribuée par l'administrataire sur demande.
<?= _('This form allows to use an approval key to validate your account. Approval keys are distributed by an administrator upon request.') ?>
</p>
<form method="post">
<label for="key">Clé d'approbation</label><br>
<label for="key"><?= _('Approval key') ?></label><br>
<input required="" id="key" size="33" name="key" type="text" placeholder="27b81fbd8277b11ed1cf03d476cec503">
<br>
<input type="submit" value="Utiliser pour ce compte">
<input type="submit" value="<?= _('Use for this account') ?>">
</form>

View File

@ -1,30 +1,33 @@
<?php displayIndex(); ?>
<p>
<?php if (isset($_SESSION['id'])) { ?>
Vous utilisez actuellement un compte <?= (($_SESSION['type'] === 'approved') ? 'approuvé' : 'de test') ?>. Son identifiant interne est <code><?= $_SESSION['id'] ?></code>.
<?php } else { ?>
Vous n'utilisez actuellement aucun compte.
<?php } ?>
<?php if (isset($_SESSION['id'])) {
echo match ($_SESSION['type']) {
'testing' => _('You are currently using a testing account.'),
'approved' => _('You are currently using an approved account.'),
} . ' ' . sprintf(_('It\'s internal ID is %s.'), '<small><code>' . $_SESSION['id'] . '</code></small>');
} else {
echo _('You are not logged in.');
} ?>
</p>
<h2>Types de comptes</h2>
<h2><?= _('Account types') ?></h2>
<dl>
<dt><span aria-hidden="true"> </span>De test</dt>
<dt><span aria-hidden="true"> </span><em><?= _('Testing') ?></em></dt>
<dd>
C'est le type de compte par défaut, avec des fonctionnalités limitées pour éviter les abus&nbsp;:
<?= _('It\'s the default account type, with limited capabilities in order to avoid abuses:') ?>
<ul>
<li>Risque d'être supprimé n'importe quand</li>
<li><?= ((CONF['ht']['user_quota_testing'] >> 30) >= 1) ? CONF['ht']['user_quota_testing'] >> 30 . ' ' . linkToDocs('units', '<abbr title="gibioctet">Gio</abbr>') : CONF['ht']['user_quota_testing'] >> 20 . ' ' . linkToDocs('units', '<abbr title="mébioctet">Mio</abbr>') ?> de SFTP</li>
<li>Certificat Let's Encrypt de test</li>
<li><strong><?= _('May be deleted anytime') ?></strong></li>
<li><?= sprintf(_('%s of SFTP quota'), ((CONF['ht']['user_quota_testing'] >> 30) >= 1) ? CONF['ht']['user_quota_testing'] >> 30 . ' ' . _('<abbr title="gibibyte">GiB</abbr>') : CONF['ht']['user_quota_testing'] >> 20 . ' ' . _('<abbr title="mebibyte">MiB</abbr>')) ?></li>
<li><?= _('Let\'s Encrypt certificate from the staging environment (not trusted by clients)') ?></li>
</ul>
</dd>
<dt><span aria-hidden="true">👤 </span>Approuvé</dt>
<dt><span aria-hidden="true">👤 </span><em><?= _('Approved') ?></em></dt>
<dd>
C'est originellement un compte de test mais qui a été approuvé par ane administrataire, et qui a pour but d'être utilisé de façon stable&nbsp;:
<?= _('It was originally a testing account, but has been approved by an administrator, and is suited for stable usecases:') ?>
<ul>
<li><?= ((CONF['ht']['user_quota_approved'] >> 30) >= 1) ? CONF['ht']['user_quota_approved'] >> 30 . ' ' . linkToDocs('units', '<abbr title="gibioctet">Gio</abbr>') : CONF['ht']['user_quota_approved'] >> 20 . ' ' . linkToDocs('units', '<abbr title="mébioctet">Mio</abbr>') ?> de SFTP</li>
<li>Vrai certificat Let's Encrypt</li>
<li><?= sprintf(_('%s of SFTP quota'), ((CONF['ht']['user_quota_approved'] >> 30) >= 1) ? CONF['ht']['user_quota_approved'] >> 30 . ' ' . _('<abbr title="gibibyte">GiB</abbr>') : CONF['ht']['user_quota_approved'] >> 20 . ' ' . _('<abbr title="mebibyte">MiB</abbr>')) ?></li>
<li><?= _('Stable Let\'s Encrypt certificates') ?></li>
</ul>
</dd>
</dl>

View File

@ -1,13 +1,13 @@
<p>Pas de compte ? <a href="register">En créer un</a></p>
<p><?= _('Need an accout?') ?> <a href="register"><?= _('Register') ?></a></p>
<form method="post">
<label for="username">Identifiant</label><br>
<input required="" minlength="1" maxlength="1024" pattern="<?= USERNAME_REGEX ?>" id="username" name="username" type="text" placeholder="<?= PLACEHOLDER_USERNAME ?>">
<label for="username"><?= _('Username') ?></label><br>
<input required="" autocomplete="username" minlength="1" maxlength="1024" pattern="<?= USERNAME_REGEX ?>" id="username" name="username" type="text" placeholder="<?= PLACEHOLDER_USERNAME ?>">
<br>
<label for="password">Clé de passe</label><br>
<label for="password"><?= _('Password') ?></label><br>
<input required="" autocomplete="current-password" minlength="8" maxlength="1024" pattern="<?= PASSWORD_REGEX ?>" id="password" name="password" type="password" placeholder="<?= PLACEHOLDER_PASSWORD ?>">
<br>
<input type="submit" value="Connexion">
<input type="submit" value="<?= _('Log in') ?>">
</form>

View File

@ -1,13 +1,9 @@
<p>
Vous pouvez ici changer la clé de passe permettant d'accéder à votre compte Niver.
</p>
<form method="post">
<label for="current-password">Clé de passe actuelle</label><br>
<label for="current-password"><?= _('Current password') ?></label><br>
<input required="" autocomplete="current-password" minlength="8" maxlength="1024" pattern="<?= PASSWORD_REGEX ?>" id="current-password" name="current-password" type="password" placeholder="<?= PLACEHOLDER_PASSWORD ?>"><br>
<label for="new-password">Nouvelle clé de passe</label><br>
<label for="new-password"><?= _('New password') ?></label><br>
<input required="" autocomplete="new-password" minlength="8" maxlength="1024" pattern="<?= PASSWORD_REGEX ?>" id="new-password" name="new-password" type="password" placeholder="<?= PLACEHOLDER_PASSWORD ?>"><br>
<input type="submit" value="Mettre à jour">
<input type="submit" value="<?= _('Update password') ?>">
</form>

View File

@ -1,17 +1,15 @@
<p>Déjà un compte ? <a href="login">Se connecter</a></p>
<p><?= _('Already have an account?') ?> <a href="login"><?= _('Log in') ?></a></p>
<form method="post">
<label for="username">Identifiant</label>
<label for="username"><?= _('Username') ?></label>
<br>
<input id="username" minlength="1" maxlength="1024" pattern="<?= USERNAME_REGEX ?>" required="" name="username" type="text" placeholder="<?= PLACEHOLDER_USERNAME ?>"><br>
<input id="username" autocomplete="username" minlength="1" maxlength="1024" pattern="<?= USERNAME_REGEX ?>" required="" name="username" type="text" placeholder="<?= PLACEHOLDER_USERNAME ?>"><br>
<details>
<summary><label for="password">Clé de passe</label></summary>
<p>Une clé de passe sécurisée est trop compliquée à deviner pour une attaque qui testerait automatiquement plein de clés de passe tout en connaissant d'autres informations et secrets sur vous.</p>
<p>Minimum 8 caractères si elle contient minuscule, majuscule et chiffre, ou minimum 10 caractères sinon.</p>
<summary><label for="password"><?= _('Password') ?></label></summary>
<?= sprintf(_('Minimum %1$s characters, or %2$s characters if it contains lowercase, uppercase and digit.'), 10, 8) ?>
</details>
<input autocomplete="new-password" id="password" minlength="8" maxlength="1024" pattern="<?= PASSWORD_REGEX ?>" required="" name="password" type="password" placeholder="<?= PLACEHOLDER_PASSWORD ?>">
<br>
<input type="submit" value="Créer ce compte">
<input type="submit" value="<?= _('Create an account') ?>">
</form>

View File

@ -1,17 +1,10 @@
<p>
Cette action supprimera toutes les données appartenant à ce compte, y compris&nbsp;:
<?= _('This will delete every resource managed by the current account, including registered domains, hosted DNS records, websites files and cryptographic keys for Onion services and DNSSEC.') ?>
</p>
<ul>
<li>la possession et la réservation des domaines dans le registre</li>
<li>les enregistrements DNS des zones hébergées sur le serveur de noms</li>
<li>le contenu des sites</li>
<li>les paires de clés des services Onion</li>
</ul>
<form method="post">
<input type="checkbox" name="delete" id="delete" required="">
<label for="delete">Supprimer le compte actuel et toutes ses données (requis)</label>
<label for="delete"><?= _('Delete the current account and everything related (required)') ?></label>
<br>
<input type="submit" value="Supprimer">
<input type="submit" value="<?= _('Delete') ?>">
</form>

View File

@ -1,10 +1,6 @@
<p>
Vous pouvez ici changer l'identifiant permettant d'accéder à votre compte Niver.
</p>
<form method="post">
<label for="new-username">Nouvel identifiant</label><br>
<input required="" autocomplete="new-username" minlength="1" maxlength="1024" pattern="<?= USERNAME_REGEX ?>" id="new-username" name="new-username" type="text" placeholder="<?= PLACEHOLDER_USERNAME ?>"><br>
<label for="new-username"><?= _('New username') ?></label><br>
<input required="" autocomplete="username" minlength="1" maxlength="1024" pattern="<?= USERNAME_REGEX ?>" id="new-username" name="new-username" type="text" placeholder="<?= PLACEHOLDER_USERNAME ?>"><br>
<input type="submit" value="Mettre à jour">
<input type="submit" value="<?= _('Update username') ?>">
</form>

View File

@ -1,9 +1,9 @@
<p>
Ajouter sur un dossier de site un accès <?= linkToDocs('http', 'HTTP') ?> par <?= linkToDocs('dns', 'DNS') ?> et <?= linkToDocs('tls', 'TLS') ?> <?= linkToDocs('ca', 'authentifié par <em>Let\'s Encrypt</em>') ?>.
<?= _('A Let\'s Encrypt certificate will be obtained.') ?>
</p>
<p>
Le domaine doit posséder les enregistrements ci-après lors du traitement de ce formulaire.
<?= _('The domain must have the following records when the form is being processed.') ?>
</p>
<dl>
@ -22,9 +22,9 @@
</dl>
<form method="post">
<label for="domain">Domaine sur lequel répondre</label><br>
<label for="domain"><?= _('Domain') ?></label><br>
<input required="" placeholder="site.<?= PLACEHOLDER_DOMAIN ?>" id="domain" name="domain" type="text"><br>
<label for="dir">Dossier ciblé</label><br>
<label for="dir"><?= _('Target directory') ?></label><br>
<select required="" name="dir" id="dir">
<option value="" disabled="" selected="">---</option>
<?php
@ -33,5 +33,5 @@ foreach (dirsStatuses('dns') as $dir => $alreadyEnabled)
?>
</select>
<br>
<input type="submit" value="Ajouter l'accès">
<input type="submit" value="<?= _('Setup access') ?>">
</form>

View File

@ -1,9 +1,5 @@
<p>
Ajouter un accès par <?= linkToDocs('tor', 'service Onion') ?> sur un dossier.
</p>
<form method="post">
<label for="dir">Dossier ciblé</label><br>
<label for="dir"><?= _('Target directory') ?></label><br>
<select required="" name="dir" id="dir">
<option value="" disabled="" selected="">---</option>
<?php
@ -12,5 +8,5 @@ foreach (dirsStatuses('onion') as $dir => $alreadyEnabled)
?>
</select>
<br>
<input type="submit" value="Ajouter l'accès">
<input type="submit" value="<?= _('Setup access') ?>">
</form>

View File

@ -1,11 +1,7 @@
<p>
Ajouter sur un dossier de site un accès <?= linkToDocs('http', 'HTTP') ?> par sous-domaine de <code><?= CONF['ht']['subdomain_domain'] ?></code>.
</p>
<form method="post">
<label for="subdomain">Sous-domaine sur lequel répondre</label><br>
<label for="subdomain"><?= _('Subdomain') ?></label><br>
<input required="" placeholder="label" id="subdomain" name="subdomain" type="text"><code>.<?= CONF['ht']['subdomain_domain'] ?></code><br>
<label for="dir">Dossier ciblé</label><br>
<label for="dir"><?= _('Target directory') ?></label><br>
<select required="" name="dir" id="dir">
<option value="" disabled="" selected="">---</option>
<?php
@ -14,5 +10,5 @@ foreach (dirsStatuses('subdomain') as $dir => $alreadyEnabled)
?>
</select>
<br>
<input type="submit" value="Ajouter l'accès">
<input type="submit" value="<?= _('Setup access') ?>">
</form>

View File

@ -1,11 +1,7 @@
<p>
Ajouter sur un dossier de site un accès <?= linkToDocs('http', 'HTTP') ?> par sous-chemin de <code><?= CONF['ht']['subpath_domain'] ?>/</code>.
</p>
<form method="post">
<label for="path">Chemin sur lequel répondre</label><br>
<label for="path"><?= _('Path') ?></label><br>
<code>https://<?= CONF['ht']['subpath_domain'] ?>/</code><input required="" placeholder="path" id="path" name="path" type="text"><br>
<label for="dir">Dossier ciblé</label><br>
<label for="dir"><?= _('Target directory') ?></label><br>
<select required="" name="dir" id="dir">
<option value="" disabled="" selected="">---</option>
<?php
@ -14,5 +10,5 @@ foreach (dirsStatuses('subpath') as $dir => $alreadyEnabled)
?>
</select>
<br>
<input type="submit" value="Ajouter l'accès">
<input type="submit" value="<?= _('Setup access') ?>">
</form>

View File

@ -1,9 +1,5 @@
<p>
Retirer un accès HTTP d'un dossier
</p>
<form method="post">
<label for="site">Accès à retirer</label><br>
<label for="site"><?= _('Access to delete') ?></label><br>
<select required="" name="site" id="site">
<option value="" disabled="" selected="">---</option>
<?php
@ -19,5 +15,5 @@ foreach (query('select', 'sites', ['username' => $_SESSION['id'] ?? '']) as $sit
?>
</select>
<br>
<input type="submit" value="Retirer l'accès">
<input type="submit" value="<?= _('Delete access') ?>">
</form>

View File

@ -1,17 +1,16 @@
<p>
Ce service permet d'envoyer des fichiers sur le serveur par <?= linkToDocs('sftp', 'SFTP') ?> afin de les rendre accessibles par <?= linkToDocs('http', 'HTTP') ?>.
<?= _('This service allows you to send files on the server using SFTP, and to make them publicly available with HTTP.') ?>
</p>
<?php displayIndex(); ?>
<section>
<h2>Sites actuellement hébergés</h2>
<dl>
<h2><?= _('Currently hosted sites') ?></h2>
<?php
$sites = query('select', 'sites', ['username' => $_SESSION['id'] ?? '']);
if ($sites === [])
echo ' <p>Ce compte n\'héberge aucun site sur cette instance.<p>' . LF;
echo ' <p><p>' . LF;
else {
echo ' <dl>' . LF;
foreach ($sites as $site) {
@ -36,68 +35,69 @@ else {
</section>
<section>
<h2>Ajouter un accès de site</h2>
<h2><?= _('Adding a site access') ?></h2>
<p>Pour pouvoir y ajouter un accès par ce service, un site doit auparavent être téléversé dans un sous-dossier direct de l'espace SFTP. Le nom de ce sous-dossier ne peut contenir que <abbr title="abcdefghijklmnopqrstuvwxyz"><code>a</code>-<code>z</code></abbr>, <abbr title="ABCDEFGHIJKLMNOPQRSTUVWXYZ"><code>A</code>-<code>Z</code></abbr>, <abbr title="0123456789"><code>0</code>-<code>9</code></abbr>, <code>_</code> et <code>-</code>.</p>
<p><?= sprintf(_('In order to be able to setup an HTTP site with this service, a subdirectory for this site must be created inside the SFTP space first. The name of this subdirectory can only contain %1$s, %2$s, %3$s, %4$s and %5$s.'), '<abbr title="abcdefghijklmnopqrstuvwxyz"><code>a</code>-<code>z</code></abbr>', '<abbr title="ABCDEFGHIJKLMNOPQRSTUVWXYZ"><code>A</code>-<code>Z</code></abbr>', '<abbr title="0123456789"><code>0</code>-<code>9</code></abbr>', '<code>_</code>', '<code>-</code>') ?></p>
</section>
<section>
<h2>SFTP</h2>
<?php
$quota = ($_SESSION['type'] ?? '' === 'approved') ? CONF['ht']['user_quota_approved'] : CONF['ht']['user_quota_testing'];
?>
<p>
Vous avez accès à un espace <abbr title="SSH File Transfert Protocol">SFTP</abbr>, limité à <?php
$quotaSize = ($_SESSION['type'] ?? '' === 'approved') ? CONF['ht']['user_quota_approved'] : CONF['ht']['user_quota_testing'];
echo (($quotaSize >> 30) >= 1) ? $quotaSize >> 30 . ' ' . linkToDocs('units', '<abbr title="gibioctet">Gio</abbr>') : $quotaSize >> 20 . ' ' . linkToDocs('units', '<abbr title="mébioctet">Mio</abbr>')
?>. Indiquez les données ci-dessous à votre client <abbr title="SSH File Transfert Protocol">SFTP</abbr> pour y accéder.
<?= sprintf(_('The SFTP space is limited to %s. Indicate the following values to your SFTP client to access it.'), (($quota >> 30) >= 1) ? $quota >> 30 . ' ' . _('<abbr title="gibibyte">GiB</abbr>') : $quota >> 20 . ' ' . _('<abbr title="mebibyte">MiB</abbr>')) ?>
</p>
<section>
<h3>Authentifier le serveur</h3>
<h3><?= _('Authenticating the server') ?></h3>
<p>Un enregistrement SSHFP est disponible.</p>
<p><?= _('An SSHFP record is available.') ?></p>
<details>
<summary>Clé publique</summary>
<summary><?= _('Plain public key') ?></summary>
<code><?= file_get_contents(CONF['ht']['sftp_pub']) ?></code>
</details>
<details open="">
<summary>Empreinte</summary>
<summary><?= _('Public key fingerprint') ?></summary>
<code><?= file_get_contents(CONF['ht']['sftp_fp']) ?></code>
</details>
<details>
<summary>Art ASCII</summary>
<summary><?= _('ASCII art') ?></summary>
<pre><?= file_get_contents(CONF['ht']['sftp_asciiart']) ?></pre>
</details>
</section>
<section>
<h3>Se connecter au serveur</h3>
<h3><?= _('Connecting to the server') ?></h3>
<a href="sftp://<?= isset($_SESSION['display-username']) ? $_SESSION['display-username'] : '&lt;username&gt;'; ?>@<?= CONF['ht']['sftp_domain'] ?>:<?= CONF['ht']['public_sftp_port'] ?>/">sftp://<?= isset($_SESSION['display-username']) ? $_SESSION['display-username'] : '&lt;username&gt;'; ?>@<?= CONF['ht']['sftp_domain'] ?>:<?= CONF['ht']['public_sftp_port'] ?>/</a>
<dl>
<dt>Serveur</dt>
<dt><?= _('Server') ?></dt>
<dd>
<code><?= CONF['ht']['sftp_domain'] ?></code>
</dd>
<dt>Port</dt>
<dt><?= _('Port') ?></dt>
<dd>
<code><?= CONF['ht']['public_sftp_port'] ?></code><?php if (CONF['ht']['public_sftp_port'] === 22) echo " (par défaut)"; ?>
</dd>
<dt>Dossier</dt>
<dt><?= _('Directory') ?></dt>
<dd>
<code>/</code>
</dd>
<dt>Utilisataire</dt>
<dt><?= _('Username') ?></dt>
<dd>
<code><?= isset($_SESSION['display-username']) ? $_SESSION['display-username'] : '&lt;username&gt;'; ?></code>
</dd>
<dt>Clé de passe</dt>
<dt><?= _('Password') ?></dt>
<dd>
celle de votre compte
<?= _('The one of your account') ?>
</dd>
</dl>
</section>
@ -108,16 +108,17 @@ echo (($quotaSize >> 30) >= 1) ? $quotaSize >> 30 . ' ' . linkToDocs('units', '<
<h3>CSP</h3>
<p>
Une politique de sécurité du contenu (<abbr title="Content-Security-Policy">CSP</abbr>) interdit l'intégration de JavaScript ou de ressources depuis des sites distants.
</p>
<h3>Compression gzip</h3>
<p>
La compression <em>gzip</em> statique est supportée, si le client le supporte et que le fichier est disponible, <code>chemin.gz</code> est servi au lieu de <code>chemin</code>.
<?= _('A content security policy (CSP) forbids Web browsers from loading JavaScript or third-party resources.') ?>
</p>
<h3>Page d'index</h3>
<h3><?= _('gzip compression') ?></h3>
<p>
Lors d'une requête sur un dossier, le premier des fichiers suivants qui existe <em>dans ce dossier</em> est répondu&nbsp;:
<?= _('Static <em>gzip</em> compression is supported: if the client supports it and the file is available, <code>path.gz</code> is served instead of <code>path</code>.') ?>
</p>
<h3><?= _('Index page') ?></h3>
<p>
<?= _('When a request hits a directory, the first of the following files that exists <em>inside this directory</em> is served:') ?>
</p>
<ol>
<li><code>index.html</code></li>
@ -125,9 +126,9 @@ echo (($quotaSize >> 30) >= 1) ? $quotaSize >> 30 . ' ' . linkToDocs('units', '<
<li><code>index.gmi</code></li>
</ol>
<h3>Page d'erreur 404</h3>
<h3><?= _('404 error page') ?></h3>
<p>
Lors d'une requête aboutissant à une erreur <code>404</code>, le premier des fichiers suivants qui existe <em>à la racine du site</em> est répondu&nbsp;:
<?= _('When a request ends in a <code>404</code> error, the first of the following files that exists <em>at the root of the site</em> is served:') ?>
</p>
<ol>
<li><code>404.html</code></li>

View File

@ -1,23 +1,16 @@
<p>
<?= linkToDocs('record-caa', 'Documentation du type d\'enregistrement CAA') ?>
</p>
<form method="post">
<?php require 'form.ns.php'; ?>
<br>
<label for="flag">Flag</label>
<label for="flag"><?= _('Flag') ?></label>
<br>
<input id="flag" min="0" max="127" placeholder="0" name="flag" type="number">
<br>
<label for="tag">Tag</label>
<label for="tag"><?= _('Tag') ?></label>
<br>
<input id="tag" minlenght="1" maxlength="128" pattern="^[a-z]{1,128}$" placeholder="issue" name="tag" type="text">
<br>
<label for="value">Valeur</label>
<label for="value"><?= _('Value') ?></label>
<br>
<input id="value" minlenght="3" maxlength="1024" pattern="^[a-z0-9.-]{3,1024}$" placeholder="letsencrypt.org" name="value" type="text">
<br>
<input type="submit" value="Appliquer">
<input type="submit" value="<?= _('Apply') ?>">
</form>

View File

@ -1,13 +1,8 @@
<p>
<?= linkToDocs('record-cname', 'Documentation du type d\'enregistrement CNAME') ?>
</p>
<form method="post">
<?php require 'form.ns.php'; ?>
<br>
<label for="cname">Nom canonique</label>
<?php require 'form.ns.php'; ?>
<label for="cname"><?= _('Canonical name') ?></label>
<br>
<input id="cname" placeholder="main.<?= PLACEHOLDER_DOMAIN ?>" name="cname" type="text">
<br>
<input type="submit" value="Appliquer">
<input type="submit" value="<?= _('Apply') ?>">
</form>

View File

@ -1,13 +1,8 @@
<p>
<?= linkToDocs('record-dname', 'Documentation du type d\'enregistrement DNAME') ?>
</p>
<form method="post">
<?php require 'form.ns.php'; ?>
<br>
<label for="dname">Nom délégué</label>
<?php require 'form.ns.php'; ?>
<label for="dname"><?= _('Delegation name') ?></label>
<br>
<input id="dname" placeholder="main.<?= PLACEHOLDER_DOMAIN ?>" name="dname" type="text">
<br>
<input type="submit" value="Appliquer">
<input type="submit" value="<?= _('Apply') ?>">
</form>

View File

@ -1,5 +1,5 @@
<form method="post">
<label for="zone">Zone à modifier</label>
<label for="zone"><?= _('Zone to be changed') ?></label>
<br>
<select required="" name="zone" id="zone">
<option value="" disabled="" selected="">-</option>
@ -9,7 +9,7 @@ foreach (nsListUserZones() as $zone)
?>
</select>
<br>
<input type="submit" value="Afficher">
<input type="submit" value="<?= _('Display') ?>">
</form>
<?php
@ -20,11 +20,11 @@ if (isset($data['zone_content'])) { // Display zone
<form method="post">
<input type="hidden" name="zone" value="<?= $_POST['zone'] ?>">
<label for="zone-content">Nouveau contenu de la zone <code><strong><?= $_POST['zone'] ?></strong></code></label>
<label for="zone-content"><?= sprintf(_('New content of the %s zone'), '<code><strong>' . $_POST['zone'] . '</strong></code>') ?></label>
<br>
<textarea id="zone-content" name="zone-content" wrap="off" rows="<?= substr_count($data['zone_content'], LF) + 1 ?>"><?= htmlspecialchars($data['zone_content']) ?></textarea>
<br>
<input type="submit" value="Remplacer">
<input type="submit" value="<?= _('Replace') ?>">
</form>
<?php
@ -35,24 +35,23 @@ displayFinalMessage($data);
?>
<h2>Valeurs par défaut</h2>
<h2><?= _('Default values') ?></h2>
<p>Si le TTL est omis, il sera définit à <code><time datetime="PT<?= DEFAULT_TTL ?>S"><?= DEFAULT_TTL ?></time></code> secondes.</p>
<p><?= sprintf(_('If the TTL is omitted, it will default to %s seconds.'), '<code><time datetime="PT' . DEFAULT_TTL . 'S">' . DEFAULT_TTL . '</time></code>') ?></p>
<p>La précision de la classe (<code>IN</code>) est facultative.</p>
<p><?= _('Precising the class (<code>IN</code>) is optional.') ?></p>
<h2>Valeurs autorisées</h2>
<h2><?= _('Allowed values') ?></h2>
<p>La zone n'est pas autorisée à dépasser <?= ZONE_MAX_CHARACTERS ?> caractères.</p>
<p><?= sprintf(_('Submitted zone content is limited to %s characters.'), ZONE_MAX_CHARACTERS) ?></p>
<p>Les TTLs ne sont autorisés qu'entre <code><time datetime="PT<?= MIN_TTL ?>S"><?= MIN_TTL ?></time></code> et <code><time datetime="PT<?= MAX_TTL ?>S"><?= MAX_TTL ?></time></code> secondes.</p>
<p><?= sprintf(_('TTLs must last between %1$s and %2$s seconds.'), '<code><time datetime="PT' . MIN_TTL . 'S">' . MIN_TTL . '</time></code>', '<code><time datetime="PT' . MAX_TTL . 'S">' . MAX_TTL . '</time></code>') ?></p>
<p>Les seuls types dont l'édition est autorisée sont&nbsp;:</p>
<p><?= _('The only types that can be defined are:') ?></p>
<ul>
<?php
foreach (ALLOWED_TYPES as $allowed_type)
echo ' <li><code>' . $allowed_type . '</code></li>';
?>
</ul>

View File

@ -1,19 +1,18 @@
<label for="action">Action</label>
<label for="action"><?= _('Action') ?></label>
<select name="action" id="action">
<option value="add">Ajouter</option>
<option value="delete">Retirer</option>
<option value="add"><?= _('Add') ?></option>
<option value="delete"><?= _('Delete') ?></option>
</select>
<br>
<fieldset>
<legend>Domaine</legend>
<legend><?= _('Domain') ?></legend>
<div>
<label for="subdomain">Sous-domaine</label>
<label for="subdomain"><?= _('Subdomain') ?></label>
<br>
<input id="subdomain" size="16" placeholder="www" pattern="^(([a-z0-9_-]{1,63}\.?){1,127})|(@){1}$" name="subdomain" type="text">
</div>
<div>
<label for="zone">Zone</label>
<label for="zone"><?= _('Zone') ?></label>
<br>
<select required="" name="zone" id="zone">
<option value="" disabled="" selected="">-</option>
@ -27,9 +26,9 @@ foreach (nsListUserZones() as $zone)
</fieldset>
<fieldset>
<legend><abbr title="Time To Live">TTL</abbr></legend>
<legend><abbr title="Time To Live"><?= _('TTL') ?></abbr></legend>
<div>
<label for="ttl-value">Valeur</label>
<label for="ttl-value"><?= _('Value') ?></label>
<br>
<input required="" id="ttl-value" list="ttls" name="ttl-value" size="6" type="number" min="1" max="432000" value="<?= DEFAULT_TTL ?>" placeholder="<?= DEFAULT_TTL ?>">
<datalist id="ttls">
@ -43,13 +42,13 @@ foreach (nsListUserZones() as $zone)
</datalist>
</div>
<div>
<label for="ttl-multiplier">Unité</label>
<label for="ttl-multiplier"><?= _('Unit') ?></label>
<br>
<select required="" name="ttl-multiplier" id="ttl-multiplier">
<option value="1">seconde</option>
<option value="60">minute</option>
<option value="3600">heure</option>
<option value="86400">jour</option>
<option value="1"><?= _('second') ?></option>
<option value="60"><?= _('minute') ?></option>
<option value="3600"><?= _('hour') ?></option>
<option value="86400"><?= _('day') ?></option>
</select>
</div>
</fieldset>

View File

@ -1,12 +1,29 @@
<p>
Ce service permet d'héberger et de gérer les enregistrements DNS d'une <?= linkToDocs('zone', 'zone DNS') ?>.
<?= _('This service allows to host and manage DNS records inside a DNS zone.') ?>
</p>
<?php displayIndex(); ?>
<h2>Serveurs de noms</h2>
<p>Une zone hébergée sur ce service est servie par ces serveurs de noms&nbsp;:</p>
<h2><?= _('Currently hosted zones') ?></h2>
<?php
$zones = query('select', 'zones', ['username' => $_SESSION['id'] ?? ''], 'zone');
if ($zones === [])
echo '<p>∅<p>' . LF;
else {
echo '<ul>' . LF;
foreach ($zones as $zone)
echo ' <code><li>' . $zone . '</li></code>' . LF;
echo '</ul>' . LF;
}
?>
<h2><?= _('Name servers') ?></h2>
<p><?= _('A zone hosted on this service is served by these name servers:') ?></p>
<ul>
<?php
@ -14,17 +31,3 @@ foreach (CONF['ns']['servers'] as $server)
echo ' <li><code>' . $server . '</code></li>';
?>
</ul>
<h2>Zones actuellement hébergées</h2>
<?php
$zones = query('select', 'zones', ['username' => $_SESSION['id'] ?? ''], 'zone');
if ($zones === [])
echo '<p>Ce compte n\'héberge aucune zone sur cette instance.<p>' . LF;
else {
echo '<ul>' . LF;
foreach ($zones as $zone)
echo ' <code><li>' . $zone . '</li></code>' . LF;
echo '</ul>' . LF;
}

View File

@ -1,10 +1,6 @@
<p>
<?= linkToDocs('record-ip', 'Documentation des types d\'enregistrements A et AAAA') ?>
</p>
<form method="post">
<?php require 'form.ns.php'; ?>
<label for="ip">Adresse IP</label><br>
<label for="ip"><?= _('IP address') ?></label><br>
<input required="" pattern="^[a-f0-9:.]+$" id="ip" name="ip" minlength="2" maxlength="39" size="40" type="text" placeholder="<?= PLACEHOLDER_IPV6 ?> ou <?= PLACEHOLDER_IPV4 ?>"><br>
<input type="submit" value="Appliquer">
<input type="submit" value="<?= _('Apply') ?>">
</form>

View File

@ -1,82 +1,76 @@
<p>
<?= linkToDocs('record-loc', 'Documentation du type d\'enregistrement LOC') ?>
</p>
<form method="post">
<?php require 'form.ns.php'; ?>
<?php require 'form.ns.php'; ?>
<fieldset>
<legend>Latitude</legend>
<legend><?= _('Latitude') ?></legend>
<div>
<label for="lat-deg">Degrés</label>
<label for="lat-deg"><?= _('Degrees') ?></label>
<br>
<input name="lat-deg" id="lat-deg" min="0" max="90" type="number" required="">
</div>
<div>
<label for="lat-min">Minutes</label>
<label for="lat-min"><?= _('Minutes') ?></label>
<br>
<input name="lat-min" id="lat-min" min="0" max="59" placeholder="0" type="number" required="">
</div>
<div>
<label for="lat-sec">Secondes</label>
<label for="lat-sec"><?= _('Seconds') ?></label>
<br>
<input name="lat-sec" id="lat-sec" step="0.001" min="0" max="59.999" placeholder="0" type="number" required="">
</div>
<div>
<label for="lat-dir">Direction</label>
<label for="lat-dir"><?= _('Direction') ?></label>
<br>
<select required="" name="lat-dir" id="lat-dir">
<option value="" selected="" disabled="">-</option>
<option value="N">N - Nord</option>
<option value="S">S - Sud</option>
<option value="N"><code>N</code> - <?= _('North') ?></option>
<option value="S"><code>S</code> - <?= _('South') ?></option>
</select>
</div>
</fieldset>
<fieldset>
<legend>Longitude</legend>
<legend><?= _('Longitude') ?></legend>
<div>
<label for="lon-deg">Degrés</label>
<label for="lon-deg"><?= _('Degrees') ?></label>
<br>
<input name="lon-deg" id="lon-deg" min="0" max="180" type="number" required="">
</div>
<div>
<label for="lon-min">Minutes</label>
<label for="lon-min"><?= _('Minutes') ?></label>
<br>
<input name="lon-min" id="lon-min" min="0" max="59" placeholder="0" type="number" required="">
</div>
<div>
<label for="lon-sec">Secondes</label>
<label for="lon-sec"><?= _('Seconds') ?></label>
<br>
<input name="lon-sec" id="lon-sec" step="0.001" min="0" max="59.999" placeholder="0" type="number" required="">
</div>
<div>
<label for="lon-dir">Direction</label>
<label for="lon-dir"><?= _('Direction') ?></label>
<br>
<select required="" name="lon-dir" id="lon-dir">
<option value="" selected="" disabled="">-</option>
<option value="E">E - Est</option>
<option value="W">W - Ouest</option>
<option value="E"><code>E</code> - <?= _('East') ?></option>
<option value="W"><code>W</code> - <?= _('West') ?></option>
</select>
</div>
</fieldset>
<label for="alt">Altitude</label>
<label for="alt"><?= _('Altitude') ?></label>
<br>
<input name="alt" id="alt" step="0.01" min="-100000.00" max="42849672.95" type="number" required="">m
<br>
<label for="size">Taille</label>
<label for="size"><?= _('Size') ?></label>
<br>
<input name="size" id="size" min="0" max="90000000" placeholder="1" type="number">m
<br>
<label for="hp">Précision horizontale</label>
<label for="hp"><?= _('Horizontal precision') ?></label>
<br>
<input name="hp" id="hp" min="0" max="90000000" placeholder="10000" type="number" required="">m
<br>
<label for="vp">Précision verticale</label>
<label for="vp"><?= _('Vertical precision') ?></label>
<br>
<input name="vp" id="vp" min="0" max="90000000" placeholder="10" type="number" required="">m
<br>
<input type="submit" value="Appliquer">
<input type="submit" value="<?= _('Apply') ?>">
</form>

View File

@ -1,23 +1,12 @@
<p>
<?= linkToDocs('record-mx', 'Documentation du type d\'enregistrement MX') ?>
</p>
<form method="post">
<?php require 'form.ns.php'; ?>
<br>
<label for="priority">Priorité</label>
<label for="priority"><?= _('Priority') ?></label>
<br>
<input id="priority" min="0" max="65535" value="0" placeholder="0" name="priority" type="number">
<br>
<label for="host">Hôte</label>
<label for="host"><?= _('Host') ?></label>
<br>
<input id="host" placeholder="mail.<?= PLACEHOLDER_DOMAIN ?>." name="host" type="text">
<br>
<input type="submit" value="Appliquer">
<input type="submit" value="<?= _('Apply') ?>">
</form>

View File

@ -1,13 +1,8 @@
<p>
<?= linkToDocs('record-ns', 'Documentation du type d\'enregistrement NS') ?>
</p>
<form method="post">
<?php require 'form.ns.php'; ?>
<br>
<label for="ns">Serveur de nom</label>
<label for="ns"><?= _('Name server') ?></label>
<br>
<input id="ns" placeholder="ns1.<?= PLACEHOLDER_DOMAIN ?>" name="ns" type="text">
<br>
<input type="submit" value="Appliquer">
<input type="submit" value="<?= _('Apply') ?>">
</form>

View File

@ -1,15 +1,14 @@
<form method="post">
<input type="radio" name="print" id="table" value="table" checked="">
<label for="table">Tableau de mes enregistrements</label>
<label for="table"><?= _('Records table') ?></label>
<br>
<input type="radio" name="print" id="ds" value="ds">
<label for="ds">Enregistrement DS</label>
<label for="ds"><?= _('DS record') ?></label>
<br>
<input type="radio" name="print" id="raw" value="raw">
<label for="raw">Fichier de zone brut</label>
<label for="raw"><?= _('Raw zonefile') ?></label>
<br>
<label for="zone">Zone</label>
<label for="zone"><?= _('Selected zone') ?></label>
<select required="" name="zone" id="zone">
<option value="" disabled="" selected="">-</option>
<?php
@ -18,7 +17,7 @@ foreach (nsListUserZones() as $zone)
?>
</select>
<br>
<input type="submit" value="Afficher">
<input type="submit" value="<?= _('Display') ?>">
</form>
<?php
@ -30,10 +29,10 @@ if (isset($data['zone-table'])) { ?>
<table>
<tr>
<th>Domaine</th>
<th>TTL</th>
<th>Type</th>
<th>Contenu</th>
<th><?= _('Domain') ?></th>
<th><?= _('TTL') ?></th>
<th><?= _('Type') ?></th>
<th><?= _('Value') ?></th>
</tr>
<?php
foreach ($data['zone-table'] as $zone_line) {
@ -50,23 +49,23 @@ if (isset($data['zone-table'])) { ?>
if (isset($data['zone-ds'])) { ?>
<dl>
<dt>Zone</dt>
<dt><?= _('Zone') ?></dt>
<dd>
<code><?= $_POST['zone'] ?></code>
</dd>
<dt>Tag</dt>
<dt><?= _('Tag') ?></dt>
<dd>
<code><?= $data['zone-ds']['tag'] ?></code>
</dd>
<dt>Algorithme</dt>
<dt><?= _('Algorithm') ?></dt>
<dd>
<code><?= $data['zone-ds']['algo'] ?></code><?= ($data['zone-ds']['algo'] === '15') ? ' (Ed25519)' : '' ?>
</dd>
<dt>Type de condensat</dt>
<dt><?= _('Digest type') ?></dt>
<dd>
<code><?= $data['zone-ds']['digest_type'] ?></code><?= ($data['zone-ds']['digest_type'] === '2') ? ' (SHA-256)' : '' ?>
</dd>
<dt>Condensat</dt>
<dt><?= _('Digest') ?></dt>
<dd>
<code><?= $data['zone-ds']['digest'] ?></code>
</dd>

View File

@ -1,35 +1,28 @@
<p>
<?= linkToDocs('record-srv', 'Documentation du type d\'enregistrement SRV') ?>
</p>
<form method="post">
<?php require 'form.ns.php'; ?>
<br>
<label for="priority">Priorité</label>
<label for="priority"><?= _('Priority') ?></label>
<br>
<input id="priority" min="0" max="65535" value="0" placeholder="0" name="priority" type="number">
<br>
<label for="weight">Poids</label>
<label for="weight"><?= _('Weight') ?></label>
<br>
<input id="weight" min="0" max="65535" value="0" placeholder="0" name="weight" type="number">
<br>
<label for="port">Port</label>
<label for="port"><?= _('Port') ?></label>
<br>
<input id="port" min="0" max="65535" placeholder="32768" name="port" type="number">
<br>
<label for="target">Cible</label>
<label for="target"><?= _('Target') ?></label>
<br>
<input id="target" minlenght="1" maxlength="128" placeholder="service.<?= PLACEHOLDER_DOMAIN ?>." name="target" type="text">
<br>
<input type="submit" value="Appliquer">
<input type="submit" value="<?= _('Apply') ?>">
</form>

View File

@ -1,14 +1,7 @@
<p>
<?= linkToDocs('record-sshfp', 'Documentation du type d\'enregistrement SSHFP') ?>
</p>
<form method="post">
<?php require 'form.ns.php'; ?>
<br>
<label for="algo">Algorithme</label>
<label for="algo"><?= _('Algorithm') ?></label>
<br>
<select required="" name="algo" id="algo">
<option value="1">1 (RSA)</option>
@ -19,18 +12,19 @@
<br>
<label for="type">Type de hash</label>
<label for="type"><?= _('Hash type') ?></label>
<br>
<select required="" name="type" id="type">
<option value="1" disabled="">1 (SHA-1)</option>
<option value="2" selected="">2 (SHA-256)</option>
</select>
<br>
<label for="fp">Empreinte</label>
<label for="fp"><?= _('Fingerprint') ?></label>
<br>
<input required="" id="fp" size="65" minlenght="64" maxlength="64" placeholder="26e6bbb4796c4fb78632e737d31a8acaba43c3a92d9c047031f04e9b70826e1d" name="fp" type="text">
<br>
<input type="submit" value="Appliquer">
<input type="submit" value="<?= _('Apply') ?>">
</form>

View File

@ -1,13 +1,7 @@
<p>
<?= linkToDocs('record-tlsa', 'Documentation du type d\'enregistrement TLSA') ?>
</p>
<form method="post">
<?php require 'form.ns.php'; ?>
<br>
<label for="use">Utilisation</label>
<label for="use"><?= _('Use') ?></label>
<br>
<select required="" name="use" id="use">
<option value="" selected="" disabled="">-</option>
@ -16,30 +10,35 @@
<option value="2">2 (DANE-TA, <abbr title="also known as">aka<abbr> Trust Anchor Assertion)</option>
<option value="3">3 (DANE-EE, <abbr title="also known as">aka<abbr> Domain issued certificate)</option>
</select>
<br>
<label for="selector">Selecteur</label>
<label for="selector"><?= _('Selector') ?></label>
<br>
<select required="" name="selector" id="selector">
<option value="" selected="" disabled="">-</option>
<option value="0">0 (le certificat entier doit correspondre)</option>
<option value="1">1 (la clé publique du certificat doit correspondre)</option>
<option value="0">0 (<?= _('the full certificate must match') ?>)</option>
<option value="1">1 (<?= _('the certificate public key must match') ?>)</option>
</select>
<br>
<label for="type">Type de correspondance</label>
<label for="type"><?= _('Match type') ?></label>
<br>
<select required="" name="type" id="type">
<option value="" selected="" disabled="">-</option>
<option value="0">0 (certificat entier)</option>
<option value="0">0 (<?= _('full certificate') ?>)</option>
<option value="1">1 (SHA-256)</option>
<option value="2">2 (SHA-512)</option>
</select>
<br>
<label for="content">Contenu</label>
<label for="content"><?= _('Value') ?></label>
<br>
<input id="content" minlenght="3" maxlength="1024" pattern="^[a-zA-Z0-9.-]{3,1024}$" placeholder="gjioerjgioer" name="content" type="text">
<input id="content" minlenght="3" maxlength="1024" pattern="^[a-zA-Z0-9.-]{3,1024}$" name="content" type="text">
<br>
<input type="submit" value="Appliquer">
<input type="submit" value="<?= _('Apply') ?>">
</form>

View File

@ -1,13 +1,8 @@
<p>
<?= linkToDocs('record-txt', 'Documentation du type d\'enregistrement TXT') ?>
</p>
<form method="post">
<?php require 'form.ns.php'; ?>
<label for="txt"><?= _('Text') ?></label>
<br>
<label for="txt">Texte</label>
<input id="txt" minlenght="5" maxlength="8192" pattern="^[a-zA-Z0-9 .@=:!%$+/\()[\]_-]{5,8192}$" placeholder="<?= _('Some text…') ?>" name="txt" type="text">
<br>
<input id="txt" minlenght="5" maxlength="8192" pattern="^[a-zA-Z0-9 .@=:!%$+/\()[\]_-]{5,8192}$" placeholder="Du texte..." name="txt" type="text">
<br>
<input type="submit" value="Appliquer">
<input type="submit" value="<?= _('Apply') ?>">
</form>

View File

@ -1,19 +1,9 @@
<p>
Pour prouver que vous possédez bien ce domaine, il doit posséder un <?= linkToDocs('ns-record', 'enregistrement NS') ?> égal à <code><?= getAuthToken() ?>._domain-verification.<?= SERVER_NAME ?>.</code> lors du traitement de ce formulaire.
</p>
<p>
La zone sera servie par ces serveurs de noms&nbsp;:
<ul>
<?php
foreach (CONF['ns']['servers'] as $server)
echo ' <li><code>' . $server . '</code></li>';
?>
</ul>
<?= sprintf(_('To prove that you own this domain, it must have a NS record equal to %s when the form is being processed.'), '<code>' . getAuthToken() . '._domain-verification.' . SERVER_NAME . '.</code>') ?>
</p>
<form method="post">
<label for="domain">Domaine</label><br>
<label for="domain"><?= _('Domain') ?></label><br>
<input required="" placeholder="domain.<?= PLACEHOLDER_DOMAIN ?>." id="domain" name="domain" type="text"><br>
<input value="Ajouter" type="submit">
<input type="submit" value="<?= _('Add') ?>">
</form>

View File

@ -1,5 +1,5 @@
<form method="post">
<label for="zone">Zone</label>
<label for="zone"><?= _('Zone') ?></label>
<select required="" name="zone" id="zone">
<option value="" disabled="" selected="">-</option>
<?php
@ -8,5 +8,5 @@ foreach (nsListUserZones() as $zone)
?>
</select>
<br>
<input type="submit" value="Supprimer toutes les données liées à cette zone">
<input type="submit" value="<?= _('Delete everything related to this zone') ?>">
</form>

View File

@ -1,15 +1,11 @@
<p>
Ici vous pouvez indiquer au registre l'enregistrement DS d'une zone afin de permettre de déléguer la confiance <?= linkToDocs('dnssec', 'DNSSEC') ?>.
</p>
<form method="post">
<label for="action">Action</label>
<label for="action"><?= _('Action') ?></label>
<select name="action" id="action">
<option value="add">Ajouter</option>
<option value="delete">Retirer</option>
<option value="add"><?= _('Add') ?></option>
<option value="delete"><?= _('Delete') ?></option>
</select>
<br>
<label for="zone">Domaine</label>
<label for="zone"><?= _('Domain') ?></label>
<br>
<select required="" name="zone" id="zone">
<option value="" disabled="" selected="">---</option>
@ -19,14 +15,14 @@ foreach (regListUserDomains() as $domain)
?>
</select>
<br>
<label for="keytag">Tag de la clé</label>
<label for="keytag"><?= _('Key tag') ?></label>
<br>
<input required="" id="keytag" placeholder="32768" min="1" max="65535" name="keytag" type="number"><span></span>
<br>
<label for="algo">Algorithme</label>
<label for="algo"><?= _('Algorithm') ?></label>
<br>
<select required="" name="algo" id="algo">
<!-- RFC 8624 : Algorithm Implementation Requirements and Usage Guidance for DNSSEC > Algorithm Selection > DNSKEY Algorithms -->
<!-- RFC 8624: Algorithm Implementation Requirements and Usage Guidance for DNSSEC > Algorithm Selection > DNSKEY Algorithms -->
<!-- https://tools.ietf.org/html/rfc8624.html#section-3.1 -->
<option value="1" disabled="">1 (RSAMD5)</option>
<option value="3" disabled="">3 (DSA)</option>
@ -42,10 +38,10 @@ foreach (regListUserDomains() as $domain)
<option value="16">16 (ED448)</option>
</select>
<br>
<label for="dt">Type de condensat</label>
<label for="dt"><?= _('Digest type') ?></label>
<br>
<select required="" name="dt" id="dt">
<!-- RFC 8624 : Algorithm Implementation Requirements and Usage Guidance for DNSSEC > Algorithm Selection > DS and CDS Algorithms -->
<!-- RFC 8624: Algorithm Implementation Requirements and Usage Guidance for DNSSEC > Algorithm Selection > DS and CDS Algorithms -->
<!-- https://tools.ietf.org/html/rfc8624.html#section-3.3 -->
<option value="1" disabled="">1 (SHA-1)</option>
<option value="2" selected="">2 (SHA-256)</option>
@ -53,9 +49,9 @@ foreach (regListUserDomains() as $domain)
<option value="4">4 (SHA-384)</option>
</select>
<br>
<label for="key">Condensat</label>
<label for="key"><?= _('Key') ?></label>
<br>
<input id="key" required="" name="key" type="text" placeholder="018F25E4A022463478C9E30136EC53771A1704A0F0B3CE5B883AC9C8A6A55D16B638B4DE70662ACA5295D3669E7CADD9">
<br>
<input type="submit" value="Appliquer">
<input type="submit" value="<?= _('Apply') ?>">
</form>

View File

@ -1,22 +1,18 @@
<p>
<?= linkToDocs('glue-record', 'Documentation sur le glue record'); ?>
</p>
<form method="post">
<label for="action">Action</label>
<label for="action"><?= _('Action') ?></label>
<select name="action" id="action">
<option value="add">Ajouter</option>
<option value="delete">Retirer</option>
<option value="add"><?= _('Add') ?></option>
<option value="delete"><?= _('Delete') ?></option>
</select>
<fieldset>
<legend>Domaine</legend>
<div class="elForm">
<label for="subdomain">Sous-domaine</label>
<legend><?= _('Domain') ?></legend>
<div>
<label for="subdomain"><?= _('Subdomain') ?></label>
<br>
<input required="" id="subdomain" placeholder="ns1" name="subdomain" type="text">
</div>
<div class="elForm">
<label for="suffix">Domaine</label>
<div>
<label for="suffix"><?= _('Domain') ?></label>
<br>
<select required="" name="suffix" id="suffix">
<option value="" disabled="" selected="">---</option>
@ -27,8 +23,8 @@ foreach(regListUserDomains() as $suffix)
</select>
</div>
</fieldset>
<label for="ip">IP</label><br>
<input required="" pattern="^[a-f0-9:.]+$" id="ip" name="ip" minlength="2" maxlength="39" size="40" type="text" placeholder="<?= PLACEHOLDER_IPV4 ?> ou <?= PLACEHOLDER_IPV6 ?>">
<label for="ip"><?= _('IP address') ?></label><br>
<input required="" pattern="^[a-f0-9:.]+$" id="ip" name="ip" minlength="2" maxlength="39" size="40" type="text" placeholder="<?= sprintf(_('%1$s or %2$s'), PLACEHOLDER_IPV6, PLACEHOLDER_IPV4) ?>">
<br>
<input type="submit" value="Appliquer">
<input type="submit" value="<?= _('Apply') ?>">
</form>

View File

@ -1,16 +1,16 @@
<p>
Ce <?= linkToDocs('registry', 'registre') ?> permet d'obtenir un domaine se terminant par <code><?= CONF['reg']['registry'] ?></code>, par exemple <code><em>domaine</em>.<?= CONF['reg']['registry'] ?></code>.
<?= sprintf(_('This domain name registry allows to register domains ending with <code>%1$s</code>, for instance <code><em>domain</em>%1$s</code>.'), '.' . CONF['reg']['registry']) ?>
</p>
<?php displayIndex(); ?>
<h2>Domaines actuellement enregistrés</h2>
<h2><?= _('Currently registered domains') ?></h2>
<?php
$domains = query('select', 'registry', ['username' => $_SESSION['id'] ?? ''], 'domain');
if ($domains === [])
echo '<p>Ce compte n\'a aucun domaine enregistré sur <code>' . CONF['reg']['registry'] . '</code><p>' . LF;
echo '<p>∅</p>' . LF;
else {
echo '<ul>' . LF;
foreach ($domains as $domain)

View File

@ -1,15 +1,11 @@
<p>
<?= linkToDocs('record-ns', 'Documentation du type d\'enregistrement NS') ?>
</p>
<form method="post">
<label for="action">Action</label>
<label for="action"><?= _('Action') ?></label>
<select name="action" id="action">
<option value="add">Ajouter</option>
<option value="delete">Retirer</option>
<option value="add"><?= _('Add') ?></option>
<option value="delete"><?= _('Delete') ?></option>
</select>
<br>
<label for="domain">Domaine</label>
<label for="domain"><?= _('Domain') ?></label>
<br>
<select required="" name="domain" id="domain">
<option value="" disabled="" selected="">---</option>
@ -19,9 +15,9 @@ foreach (regListUserDomains() as $domain)
?>
</select>
<br>
<label for="ns">Serveur de nom</label>
<label for="ns"><?= _('Name server') ?></label>
<br>
<input id="ns" placeholder="ns1.<?= PLACEHOLDER_DOMAIN ?>." name="ns" type="text">
<br>
<input type="submit" value="Appliquer">
<input type="submit" value="<?= _('Apply') ?>">
</form>

View File

@ -1,5 +1,5 @@
<form method="post">
<label for="domain">Domaine</label>
<label for="domain"><?= _('Domain') ?></label>
<select required="" name="domain" id="domain">
<option value="" disabled="" selected="">-</option>
<?php
@ -8,15 +8,15 @@ foreach (regListUserDomains() as $domain)
?>
</select>
<br>
<input type="submit" value="Afficher">
<input type="submit" value="<?= _('Display') ?>">
</form>
<table>
<tr>
<th>Domaine</th>
<th>TTL</th>
<th>Type</th>
<th>Contenu</th>
<th><?= _('Domain') ?></th>
<th><?= _('TTL') ?></th>
<th><?= _('Type') ?></th>
<th><?= _('Value') ?></th>
</tr>
<?php

View File

@ -1,11 +1,11 @@
<p>
Enregistrer un nouveau domaine sur son compte. Ce domaine doit être composé uniquement d'au moins 4 lettres latines non accentuées (a-z).
<?= _('Register a new domain on your account. It must consist of between 4 and 63 letters and digits.') ?>
</p>
<form method="post">
<label for="subdomain">Sous-domaine</label>
<label for="subdomain"><?= _('Subdomain') ?></label>
<br>
<code><input id="subdomain" pattern="<?= SUBDOMAIN_REGEX ?>" required="" placeholder="niver" name="subdomain" type="text">.<?= CONF['reg']['registry'] ?></code>
<br>
<input type="submit" value="Enregistrer">
<input type="submit" value="<?= _('Register') ?>">
</form>

View File

@ -1,242 +0,0 @@
# List of subdomains not available to register
#
# They may be forbidden because:
# - they may be privileged for impersonating Niver, spamming or fishing
# - they are reserved for a project asking for it and deserving such a well-known name
niver
# Registry-related
nic
domain
domains
reg
registry
# Special subdomains
autoconfig
autodiscover
# Special TLDs
example
invalid
test
local
localhost
onion
# Standard-related
ns0
ns1
ns2
ns3
ns4
ns5
ns6
ns7
ns8
ns9
dns
dns0
dns1
dns2
dns3
dns4
dns5
dns6
dns7
dns8
dns9
www
wwww
www0
www1
www2
www3
www4
www5
www6
www7
www8
www9
srv
srv0
srv1
srv2
srv3
srv4
srv5
srv6
srv7
srv8
srv9
ssh
sftp
http
https
ssl
tls
mtx
matrix
gmi
gemini
ftp
ftps
mx
imap
imaps
smtp
smtps
pop
xmpp
fedi
html
rss
ipv4
ipv6
# Prevent account fishing
account
accounts
register
profile
signup
login
auth
authenticate
connect
# Commercial
com
free
trial
ads
bank
banks
business
customer
customers
store
stores
shop
shops
job
jobs
marketing
sales
# Miscellaneous
org
net
com
gov
gouv
edu
api
cdn
support
admin
web
dev
host
portal
beta
alpha
demo
vpn
temp
root
data
stats
chat
about
remote
portal
boost
core
learn
community
meta
news
public
online
join
mobile
tech
space
zone
name
access
search
static
secure
security
bbs
help
info
code
doc
docs
server
servers
client
clients
mail
mails
email
emails
webmail
site
sites
website
websites
blog
blogs
gemlog
gemlogs
capsule
capsules
source
sources
update
updates
forum
forums
service
services
ressource
ressources
image
images
video
videos
radio
radios
music
map
maps
app
apps
dev
devs
developer
developers
social
cloud
clouds
network
networks
survey
surveys
build
builds
upload
uploads
download
downloads
content
contents
drive
drives
home
homes

View File

@ -1,11 +1,11 @@
<p>
Pour prouver que vous êtes autorisé à recevoir le domaine par san possessaire actuele, ledit domaine doit posséder un <?= linkToDocs('ns-record', 'enregistrement NS') ?> égal à <code><?= getAuthToken() ?>._transfer-verification.<?= SERVER_NAME ?>.</code> lors du traitement de ce formulaire. Cet enregistrement sera automatiquement retiré une fois validé.
<?= sprintf(_('To prove that you are allowed to receive the domain by its current owner, the domain must have an NS record equal to %s when the form is being processed. The NS record will be automatically deleted once validated.'), '<code>' . getAuthToken() . '._transfer-verification.' . SERVER_NAME '.</code>') ?>
</p>
<form method="post">
<label for="subdomain">Sous-domaine à recevoir</label>
<label for="subdomain"><?= _('Subdomain that will be transferred to this account') ?></label>
<br>
<code><input required="" placeholder="subdomain" id="subdomain" name="subdomain" type="text">.<?= CONF['reg']['registry'] ?></code>
<code><input required="" pattern="<?= SUBDOMAIN_REGEX ?>" placeholder="subdomain" id="subdomain" name="subdomain" type="text">.<?= CONF['reg']['registry'] ?></code>
<br>
<input type="submit" value="Recevoir ce domaine">
<input type="submit" value="<?= _('Receive the domain') ?>">
</form>

View File

@ -1,9 +1,9 @@
<p>
Ceci désenregistrera le domaine, et le rendra ainsi à nouveau disponible à l'enregistrement par n'importe qui.
<?= _('This will unregister the domain, making it registerable by anyone again.') ?>
</p>
<form method="post">
<label for="domain">Domaine</label>
<label for="domain"><?= _('Domain') ?></label>
<br>
<select required="" name="domain" id="domain">
<option value="" disabled="" selected="">---</option>
@ -13,5 +13,5 @@ foreach(regListUserDomains() as $domain)
?>
</select>
<br>
<input type="submit" value="Effacer et redisponibiliser au public">
<input type="submit" value="<?= _('Unregister') ?>">
</form>

View File

@ -3,9 +3,21 @@ define('CONF', parse_ini_file(__DIR__ . '/config.ini', true, INI_SCANNER_TYPED))
define('DB', new PDO('sqlite:' . CONF['common']['root_path'] . '/db/niver.db'));
foreach (array_diff(scandir(CONF['common']['root_path'] . '/fn'), array('..', '.')) as $file)
require CONF['common']['root_path'] . '/fn/' . $file;
require 'pages.php';
$locale = 'en';
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
$client_locales = explode(',', preg_replace('/[A-Z0-9]|q=|;|-|\./', '', $_SERVER['HTTP_ACCEPT_LANGUAGE']));
$available_locales = array_diff(scandir('locales'), ['..', '.']);
foreach ($client_locales as $client_locale) {
if (in_array($client_locale, $available_locales)) {
$locale = $client_locale;
break;
}
}
}
define('LOCALE', $locale);
setlocale(LC_MESSAGES, 'C.UTF-8');
bindtextdomain('messages', 'locales/' . LOCALE);
header('Content-Language: ' . LOCALE);
const LF = "\n";
@ -13,6 +25,11 @@ const PLACEHOLDER_DOMAIN = 'example'; // From RFC2606: Reserved Top Level DNS Na
const PLACEHOLDER_IPV6 = '2001:db8::3'; // From RFC3849: IPv6 Address Prefix Reserved for Documentation
const PLACEHOLDER_IPV4 = '203.0.113.42'; // From RFC5737: IPv4 Address Blocks Reserved for Documentation
foreach (array_diff(scandir(CONF['common']['root_path'] . '/fn'), array('..', '.')) as $file)
require CONF['common']['root_path'] . '/fn/' . $file;
require 'pages.php';
if ($_SERVER['REQUEST_URI'] === '/sftpgo-auth.php')
return;
@ -100,9 +117,9 @@ function displayFinalMessage($data) {
if ($_POST !== []) {
if (PAGE_METADATA['require-login'] ?? true !== false) {
if (isset($_SESSION['id']) !== true)
output(403, 'Vous devez être connecté·e à un compte pour effectuer cette action.');
output(403, _('You need to be logged in to do this.'));
if (isset(query('select', 'users', ['id' => $_SESSION['id']], 'id')[0]) !== true)
output(403, 'Ce compte n\'existe plus. Déconnectez-vous pour terminer cette session fantôme.');
output(403, _('This account doesn\'t exist anymore. Log out to end this ghost session.'));
}
if (file_exists('pg-act/' . PAGE_ADDRESS . '.php'))
require 'pg-act/' . PAGE_ADDRESS . '.php';

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="fr"<?php if (!empty(SERVICE)) echo ' class="' . SERVICE . '"'; ?>>
<html lang="<?= LOCALE ?>"<?php if (!empty(SERVICE)) echo ' class="' . SERVICE . '"'; ?>>
<head>
<meta charset="utf-8">
<title><?php
@ -16,10 +16,9 @@
<header>
<p>
<?php if (isset($_SESSION['id'])) { ?>
<?= ($_SESSION['type'] === 'approved') ? '<span title="Compte approuvé">👤 </span>' : '<span title="Compte de test">⏳ </span>' ?><strong><?= (defined('DISPLAY_USERNAME')
? DISPLAY_USERNAME : '<em>?</em>') ?></strong> <a class="auth" href="<?= CONF['common']['prefix'] ?>/auth/logout">Se déconnecter</a>
<span aria-hidden="true"><?= ($_SESSION['type'] === 'approved') ? '👤' : '⏳' ?> </span><strong><?= (defined('DISPLAY_USERNAME') ? DISPLAY_USERNAME : '<em>?</em>') ?></strong> <a class="auth" href="<?= CONF['common']['prefix'] ?>/auth/logout"><?= _('Log out') ?></a>
<?php } else { ?>
<span aria-hidden="true">👻 </span><em>Anonyme</em> <a class="auth" href="<?= redirUrl('auth/login') ?>">Se connecter</a>
<span aria-hidden="true">👻 </span><em><?= _('Anonymous') ?></em> <a class="auth" href="<?= redirUrl('auth/login') ?>"><?= _('Log in') ?></a>
<?php } ?>
</p>
<nav>
@ -39,14 +38,14 @@
require 'pg-view/' . PAGE_ADDRESS . '.php';
if ($_POST === [] AND PAGE_METADATA['require-login'] ?? true !== false AND !isset($_SESSION['id']) AND PAGE_TERMINAL)
echo '<p>Ce formulaire ne sera pas accepté car il faut <a class="auth" href="' . redirUrl('auth/login') . '">se connecter</a> avant.</p>';
echo '<p>' . sprintf(_('This form won\'t be accepted because you need to %slog in%s first.'), '<a class="auth" href="' . redirUrl('auth/login') . '">', '</a>') . '</p>';
displayFinalMessage($data);
?>
</main>
<footer>
<small><a rel="external" href="https://code.antopie.org/niver/niver" class="niver">Code source</a> sous <abbr title="Cooperative Nonviolent Public License No Attribution version 7 ou plus">CNPL-NAv7+</abbr>.</small>
<small><?= sprintf(_('%sSource code%s available under %s.'), '<a rel="external" href="https://code.antopie.org/niver/niver" class="niver">', '</a>', '<abbr title="Cooperative Nonviolent Public License No Attribution version 7 or more">CNPL-NAv7+</abbr>') ?></small>
</footer>
</body>
</html>