aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2015-07-23 09:28:58 +0200
committerGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2015-07-23 09:28:58 +0200
commit28dd652228b8e863cddd3d03613c586c01796204 (patch)
treed6d83d44beb2a31095ca301723fe7ab79ee87e37
parentfa76e863c5622609b944542b0255c30f2f67c97e (diff)
parent1ad36c420e774d6f66b6a7ecf5b37ed1103ea464 (diff)
Merge pull request #914 from marienfressinaud/679-create-accounts
Add account creation feature. To be tested for 1.1.2-beta
-rw-r--r--app/Controllers/authController.php11
-rw-r--r--app/Controllers/userController.php101
-rw-r--r--app/Models/ConfigurationSetter.php7
-rw-r--r--app/i18n/cz/admin.php7
-rw-r--r--app/i18n/cz/conf.php4
-rw-r--r--app/i18n/cz/feedback.php1
-rw-r--r--app/i18n/cz/gen.php18
-rw-r--r--app/i18n/de/admin.php7
-rw-r--r--app/i18n/de/conf.php4
-rw-r--r--app/i18n/de/feedback.php1
-rw-r--r--app/i18n/de/gen.php18
-rw-r--r--app/i18n/en/admin.php7
-rw-r--r--app/i18n/en/conf.php4
-rw-r--r--app/i18n/en/feedback.php1
-rw-r--r--app/i18n/en/gen.php18
-rw-r--r--app/i18n/fr/admin.php7
-rw-r--r--app/i18n/fr/conf.php4
-rw-r--r--app/i18n/fr/feedback.php1
-rw-r--r--app/i18n/fr/gen.php18
-rw-r--r--app/views/auth/formLogin.phtml4
-rw-r--r--app/views/auth/personaLogin.phtml4
-rw-r--r--app/views/auth/register.phtml38
-rw-r--r--app/views/auth/reset.phtml2
-rw-r--r--app/views/user/manage.phtml28
-rw-r--r--app/views/user/profile.phtml37
-rw-r--r--data/config.default.php4
-rw-r--r--lib/lib_rss.php16
27 files changed, 346 insertions, 26 deletions
diff --git a/app/Controllers/authController.php b/app/Controllers/authController.php
index b55892475..aff184263 100644
--- a/app/Controllers/authController.php
+++ b/app/Controllers/authController.php
@@ -346,4 +346,15 @@ class FreshRSS_auth_Controller extends Minz_ActionController {
}
}
}
+
+ /**
+ * This action gives possibility to a user to create an account.
+ */
+ public function registerAction() {
+ if (max_registrations_reached()) {
+ Minz_Error::error(403);
+ }
+
+ Minz_View::prependTitle(_t('gen.auth.registration.title') . ' · ');
+ }
}
diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php
index ed01b83c5..428cd145d 100644
--- a/app/Controllers/userController.php
+++ b/app/Controllers/userController.php
@@ -12,9 +12,14 @@ class FreshRSS_user_Controller extends Minz_ActionController {
* This action is called before every other action in that class. It is
* the common boiler plate for every action. It is triggered by the
* underlying framework.
+ *
+ * @todo clean up the access condition.
*/
public function firstAction() {
- if (!FreshRSS_Auth::hasAccess()) {
+ if (!FreshRSS_Auth::hasAccess() && !(
+ Minz_Request::actionName() === 'create' &&
+ !max_registrations_reached()
+ )) {
Minz_Error::error(403);
}
}
@@ -25,13 +30,17 @@ class FreshRSS_user_Controller extends Minz_ActionController {
public function profileAction() {
Minz_View::prependTitle(_t('conf.profile.title') . ' · ');
+ Minz_View::appendScript(Minz_Url::display(
+ '/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js')
+ ));
+
if (Minz_Request::isPost()) {
$ok = true;
- $passwordPlain = Minz_Request::param('passwordPlain', '', true);
+ $passwordPlain = Minz_Request::param('newPasswordPlain', '', true);
if ($passwordPlain != '') {
- Minz_Request::_param('passwordPlain'); //Discard plain-text password ASAP
- $_POST['passwordPlain'] = '';
+ Minz_Request::_param('newPasswordPlain'); //Discard plain-text password ASAP
+ $_POST['newPasswordPlain'] = '';
if (!function_exists('password_hash')) {
include_once(LIB_PATH . '/password_compat.php');
}
@@ -103,8 +112,24 @@ class FreshRSS_user_Controller extends Minz_ActionController {
$this->view->size_user = $entryDAO->size();
}
+ /**
+ * This action creates a new user.
+ *
+ * Request parameters are:
+ * - new_user_language
+ * - new_user_name
+ * - new_user_passwordPlain
+ * - new_user_email
+ * - r (i.e. a redirection url, optional)
+ *
+ * @todo clean up this method. Idea: write a method to init a user with basic information.
+ * @todo handle r redirection in Minz_Request::forward directly?
+ */
public function createAction() {
- if (Minz_Request::isPost() && FreshRSS_Auth::hasAccess('admin')) {
+ if (Minz_Request::isPost() && (
+ FreshRSS_Auth::hasAccess('admin') ||
+ !max_registrations_reached()
+ )) {
$db = FreshRSS_Context::$system_conf->db;
require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php');
@@ -175,15 +200,37 @@ class FreshRSS_user_Controller extends Minz_ActionController {
Minz_Session::_param('notification', $notif);
}
- Minz_Request::forward(array('c' => 'user', 'a' => 'manage'), true);
+ $redirect_url = urldecode(Minz_Request::param('r', false, true));
+ if (!$redirect_url) {
+ $redirect_url = array('c' => 'user', 'a' => 'manage');
+ }
+ Minz_Request::forward($redirect_url, true);
}
+ /**
+ * This action delete an existing user.
+ *
+ * Request parameter is:
+ * - username
+ *
+ * @todo clean up this method. Idea: create a User->clean() method.
+ */
public function deleteAction() {
- if (Minz_Request::isPost() && FreshRSS_Auth::hasAccess('admin')) {
+ $username = Minz_Request::param('username');
+ $redirect_url = urldecode(Minz_Request::param('r', false, true));
+ if (!$redirect_url) {
+ $redirect_url = array('c' => 'user', 'a' => 'manage');
+ }
+
+ $self_deletion = Minz_Session::param('currentUser', '_') === $username;
+
+ if (Minz_Request::isPost() && (
+ FreshRSS_Auth::hasAccess('admin') ||
+ $self_deletion
+ )) {
$db = FreshRSS_Context::$system_conf->db;
require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php');
- $username = Minz_Request::param('username');
$ok = ctype_alnum($username);
$user_data = join_path(DATA_PATH, 'users', $username);
@@ -191,6 +238,16 @@ class FreshRSS_user_Controller extends Minz_ActionController {
$default_user = FreshRSS_Context::$system_conf->default_user;
$ok &= (strcasecmp($username, $default_user) !== 0); //It is forbidden to delete the default user
}
+ if ($ok && $self_deletion) {
+ // We check the password if it's a self-destruction
+ $nonce = Minz_Session::param('nonce');
+ $challenge = Minz_Request::param('challenge', '');
+
+ $ok &= FreshRSS_FormAuth::checkCredentials(
+ $username, FreshRSS_Context::$user_conf->passwordHash,
+ $nonce, $challenge
+ );
+ }
if ($ok) {
$ok &= is_dir($user_data);
}
@@ -200,6 +257,10 @@ class FreshRSS_user_Controller extends Minz_ActionController {
$ok &= recursive_unlink($user_data);
//TODO: delete Persona file
}
+ if ($ok && $self_deletion) {
+ FreshRSS_Auth::removeAccess();
+ $redirect_url = array('c' => 'index', 'a' => 'index');
+ }
invalidateHttpCache();
$notif = array(
@@ -209,6 +270,30 @@ class FreshRSS_user_Controller extends Minz_ActionController {
Minz_Session::_param('notification', $notif);
}
+ Minz_Request::forward($redirect_url, true);
+ }
+
+ /**
+ * This action updates the max number of registrations.
+ *
+ * Request parameter is:
+ * - max-registrations (int >= 0)
+ */
+ public function setRegistrationAction() {
+ if (Minz_Request::isPost() && FreshRSS_Auth::hasAccess('admin')) {
+ $limits = FreshRSS_Context::$system_conf->limits;
+ $limits['max_registrations'] = Minz_Request::param('max-registrations', 1);
+ FreshRSS_Context::$system_conf->limits = $limits;
+ FreshRSS_Context::$system_conf->save();
+
+ invalidateHttpCache();
+
+ Minz_Session::_param('notification', array(
+ 'type' => 'good',
+ 'content' => _t('feedback.user.set_registration')
+ ));
+ }
+
Minz_Request::forward(array('c' => 'user', 'a' => 'manage'), true);
}
}
diff --git a/app/Models/ConfigurationSetter.php b/app/Models/ConfigurationSetter.php
index 4bd29ecb0..d7378d4d8 100644
--- a/app/Models/ConfigurationSetter.php
+++ b/app/Models/ConfigurationSetter.php
@@ -352,6 +352,9 @@ class FreshRSS_ConfigurationSetter {
'min' => 0,
'max' => $max_small_int,
),
+ 'max_registrations' => array(
+ 'min' => 0,
+ ),
);
foreach ($values as $key => $value) {
@@ -361,8 +364,8 @@ class FreshRSS_ConfigurationSetter {
$limits = $limits_keys[$key];
if (
- (!isset($limits['min']) || $value > $limits['min']) &&
- (!isset($limits['max']) || $value < $limits['max'])
+ (!isset($limits['min']) || $value >= $limits['min']) &&
+ (!isset($limits['max']) || $value <= $limits['max'])
) {
$data['limits'][$key] = $value;
}
diff --git a/app/i18n/cz/admin.php b/app/i18n/cz/admin.php
index b9ef707cf..bfa67573e 100644
--- a/app/i18n/cz/admin.php
+++ b/app/i18n/cz/admin.php
@@ -160,8 +160,15 @@ return array(
'create' => 'Vytvořit nového uživatele',
'email_persona' => 'Email pro přihlášení<br /><small>(pro <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
'language' => 'Jazyk',
+ 'number' => 'There is %d account created yet', // TODO: translate
+ 'numbers' => 'There are %d accounts created yet', // TODO: translate
'password_form' => 'Heslo<br /><small>(pro přihlášení webovým formulářem)</small>',
'password_format' => 'Alespoň 7 znaků',
+ 'registration' => array(
+ 'allow' => 'Allow account creation', // TODO: translate
+ 'help' => '0 means that there is no account limit', // TODO: translate
+ 'number' => 'Max number of accounts', // TODO: translate
+ ),
'title' => 'Správa uživatelů',
'user_list' => 'Seznam uživatelů',
'username' => 'Přihlašovací jméno',
diff --git a/app/i18n/cz/conf.php b/app/i18n/cz/conf.php
index 9518df66d..859eeac89 100644
--- a/app/i18n/cz/conf.php
+++ b/app/i18n/cz/conf.php
@@ -72,6 +72,10 @@ return array(
),
'profile' => array(
'_' => 'Správa profilu',
+ 'delete' => array(
+ '_' => 'Account deletion', // TODO: translate
+ 'warn' => 'Your account and all the related data will be deleted.', // TODO: translate
+ ),
'email_persona' => 'Email pro přihlášení<br /><small>(pro <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
'password_api' => 'Password API<br /><small>(tzn. pro mobilní aplikace)</small>',
'password_form' => 'Heslo<br /><small>(pro přihlášení webovým formulářem)</small>',
diff --git a/app/i18n/cz/feedback.php b/app/i18n/cz/feedback.php
index b75a4a15a..cf2ee518c 100644
--- a/app/i18n/cz/feedback.php
+++ b/app/i18n/cz/feedback.php
@@ -102,6 +102,7 @@ return array(
'_' => 'Uživatel %s byl smazán',
'error' => 'Uživatele %s nelze smazat',
),
+ 'set_registration' => 'The maximum amount of accounts has been updated.', // TODO: translate
),
'profile' => array(
'error' => 'Váš profil nelze změnit',
diff --git a/app/i18n/cz/gen.php b/app/i18n/cz/gen.php
index 7c333a9c8..53127998f 100644
--- a/app/i18n/cz/gen.php
+++ b/app/i18n/cz/gen.php
@@ -21,15 +21,27 @@ return array(
'truncate' => 'Smazat všechny články',
),
'auth' => array(
+ 'email' => 'Email',
'keep_logged_in' => 'Zapamatovat přihlášení <small>(1 měsíc)</small>',
'login' => 'Login',
'login_persona' => 'Přihlášení pomocí Persona',
'login_persona_problem' => 'Problém s připojením k Persona?',
'logout' => 'Odhlášení',
- 'password' => 'Heslo',
+ 'password' => array(
+ '_' => 'Heslo',
+ 'format' => '<small>Alespoň 7 znaků</small>',
+ ),
+ 'registration' => array(
+ '_' => 'New account', // TODO: translate
+ 'ask' => 'Create an account?', // TODO: translate
+ 'title' => 'Account creation', // TODO: translate
+ ),
'reset' => 'Reset přihlášení',
- 'username' => 'Uživatel',
- 'username_admin' => 'Název administrátorského účtu',
+ 'username' => array(
+ '_' => 'Uživatel',
+ 'admin' => 'Název administrátorského účtu',
+ 'format' => '<small>maximálně 16 alfanumerických znaků</small>',
+ ),
'will_reset' => 'Přihlašovací systém bude vyresetován: místo sytému Persona bude použito přihlášení formulářem.',
),
'date' => array(
diff --git a/app/i18n/de/admin.php b/app/i18n/de/admin.php
index c0cbf6787..667f7af8d 100644
--- a/app/i18n/de/admin.php
+++ b/app/i18n/de/admin.php
@@ -160,8 +160,15 @@ return array(
'create' => 'Neuen Benutzer erstellen',
'email_persona' => 'Anmelde-E-Mail-Adresse<br /><small>(für <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
'language' => 'Sprache',
+ 'number' => 'There is %d account created yet', // TODO: translate
+ 'numbers' => 'There are %d accounts created yet', // TODO: translate
'password_form' => 'Passwort<br /><small>(für die Anmeldemethode per Webformular)</small>',
'password_format' => 'mindestens 7 Zeichen',
+ 'registration' => array(
+ 'allow' => 'Allow account creation', // TODO: translate
+ 'help' => '0 means that there is no account limit', // TODO: translate
+ 'number' => 'Max number of accounts', // TODO: translate
+ ),
'title' => 'Benutzer verwalten',
'user_list' => 'Liste der Benutzer',
'username' => 'Nutzername',
diff --git a/app/i18n/de/conf.php b/app/i18n/de/conf.php
index 4a0a77ddd..5313ec3fd 100644
--- a/app/i18n/de/conf.php
+++ b/app/i18n/de/conf.php
@@ -72,6 +72,10 @@ return array(
),
'profile' => array(
'_' => 'Profil-Verwaltung',
+ 'delete' => array(
+ '_' => 'Account deletion', // TODO: translate
+ 'warn' => 'Your account and all the related data will be deleted.', // TODO: translate
+ ),
'email_persona' => 'Anmelde-E-Mail-Adresse<br /><small>(für <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
'password_api' => 'Passwort-API<br /><small>(z. B. für mobile Anwendungen)</small>',
'password_form' => 'Passwort<br /><small>(für die Anmeldemethode per Webformular)</small>',
diff --git a/app/i18n/de/feedback.php b/app/i18n/de/feedback.php
index 4c15aadc3..61a7d9d61 100644
--- a/app/i18n/de/feedback.php
+++ b/app/i18n/de/feedback.php
@@ -102,6 +102,7 @@ return array(
'_' => 'Der Benutzer %s ist gelöscht worden',
'error' => 'Der Benutzer %s kann nicht gelöscht werden',
),
+ 'set_registration' => 'The maximum amount of accounts has been updated.', // TODO: translate
),
'profile' => array(
'error' => 'Ihr Profil kann nicht geändert werden',
diff --git a/app/i18n/de/gen.php b/app/i18n/de/gen.php
index f24a52c2e..f8f4823a6 100644
--- a/app/i18n/de/gen.php
+++ b/app/i18n/de/gen.php
@@ -21,15 +21,27 @@ return array(
'truncate' => 'Alle Artikel löschen',
),
'auth' => array(
+ 'email' => 'E-Mail-Adresse',
'keep_logged_in' => 'Eingeloggt bleiben <small>(1 Monat)</small>',
'login' => 'Anmelden',
'login_persona' => 'Anmelden mit Persona',
'login_persona_problem' => 'Verbindungsproblem mit Persona?',
'logout' => 'Abmelden',
- 'password' => 'Passwort',
+ 'password' => array(
+ '_' => 'Passwort',
+ 'format' => '<small>mindestens 7 Zeichen</small>',
+ ),
+ 'registration' => array(
+ '_' => 'New account', // TODO: translate
+ 'ask' => 'Create an account?', // TODO: translate
+ 'title' => 'Account creation', // TODO: translate
+ ),
'reset' => 'Zurücksetzen der Authentifizierung',
- 'username' => 'Nutzername',
- 'username_admin' => 'Administrator-Nutzername',
+ 'username' => array(
+ '_' => 'Nutzername',
+ 'admin' => 'Administrator-Nutzername',
+ 'format' => '<small>maximal 16 alphanumerische Zeichen</small>',
+ ),
'will_reset' => 'Authentifikationssystem wird zurückgesetzt: ein Formular wird anstelle von Persona benutzt.',
),
'date' => array(
diff --git a/app/i18n/en/admin.php b/app/i18n/en/admin.php
index 155384afd..aeea61631 100644
--- a/app/i18n/en/admin.php
+++ b/app/i18n/en/admin.php
@@ -160,8 +160,15 @@ return array(
'create' => 'Create new user',
'email_persona' => 'Login mail address<br /><small>(for <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
'language' => 'Language',
+ 'number' => 'There is %d account created yet',
+ 'numbers' => 'There are %d accounts created yet',
'password_form' => 'Password<br /><small>(for the Web-form login method)</small>',
'password_format' => 'At least 7 characters',
+ 'registration' => array(
+ 'allow' => 'Allow account creation',
+ 'help' => '0 means that there is no account limit',
+ 'number' => 'Max number of accounts',
+ ),
'title' => 'Manage users',
'user_list' => 'List of users',
'username' => 'Username',
diff --git a/app/i18n/en/conf.php b/app/i18n/en/conf.php
index 42a06906d..69162932f 100644
--- a/app/i18n/en/conf.php
+++ b/app/i18n/en/conf.php
@@ -72,6 +72,10 @@ return array(
),
'profile' => array(
'_' => 'Profile management',
+ 'delete' => array(
+ '_' => 'Account deletion',
+ 'warn' => 'Your account and all the related data will be deleted.',
+ ),
'email_persona' => 'Login email address<br /><small>(for <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
'password_api' => 'Password API<br /><small>(e.g., for mobile apps)</small>',
'password_form' => 'Password<br /><small>(for the Web-form login method)</small>',
diff --git a/app/i18n/en/feedback.php b/app/i18n/en/feedback.php
index c9189c0d0..c9f73dc1d 100644
--- a/app/i18n/en/feedback.php
+++ b/app/i18n/en/feedback.php
@@ -102,6 +102,7 @@ return array(
'_' => 'User %s has been deleted',
'error' => 'User %s cannot be deleted',
),
+ 'set_registration' => 'The maximum amount of accounts has been updated.',
),
'profile' => array(
'error' => 'Your profile cannot be modified',
diff --git a/app/i18n/en/gen.php b/app/i18n/en/gen.php
index 6e47e0921..1feb8d6ac 100644
--- a/app/i18n/en/gen.php
+++ b/app/i18n/en/gen.php
@@ -21,15 +21,27 @@ return array(
'truncate' => 'Delete all articles',
),
'auth' => array(
+ 'email' => 'Email address',
'keep_logged_in' => 'Keep me logged in <small>(1 month)</small>',
'login' => 'Login',
'login_persona' => 'Login with Persona',
'login_persona_problem' => 'Connection problem with Persona?',
'logout' => 'Logout',
- 'password' => 'Password',
+ 'password' => array(
+ '_' => 'Password',
+ 'format' => '<small>At least 7 characters</small>',
+ ),
+ 'registration' => array(
+ '_' => 'New account',
+ 'ask' => 'Create an account?',
+ 'title' => 'Account creation',
+ ),
'reset' => 'Authentication reset',
- 'username' => 'Username',
- 'username_admin' => 'Administrator username',
+ 'username' => array(
+ '_' => 'Username',
+ 'admin' => 'Administrator username',
+ 'format' => '<small>maximum 16 alphanumeric characters</small>',
+ ),
'will_reset' => 'Authentication system will be reset: a form will be used instead of Persona.',
),
'date' => array(
diff --git a/app/i18n/fr/admin.php b/app/i18n/fr/admin.php
index b740bd0d2..01e0cb3c7 100644
--- a/app/i18n/fr/admin.php
+++ b/app/i18n/fr/admin.php
@@ -160,8 +160,15 @@ return array(
'create' => 'Créer un nouvel utilisateur',
'email_persona' => 'Adresse courriel de connexion<br /><small>(pour <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
'language' => 'Langue',
+ 'number' => '%d compte a déjà été créé',
+ 'numbers' => '%d comptes ont déjà été créés',
'password_form' => 'Mot de passe<br /><small>(pour connexion par formulaire)</small>',
'password_format' => '7 caractères minimum',
+ 'registration' => array(
+ 'allow' => 'Autoriser la création de comptes',
+ 'help' => 'Un chiffre de 0 signifie que l’on peut créer un nombre infini de comptes',
+ 'number' => 'Nombre max de comptes',
+ ),
'title' => 'Gestion des utilisateurs',
'user_list' => 'Liste des utilisateurs',
'username' => 'Nom d’utilisateur',
diff --git a/app/i18n/fr/conf.php b/app/i18n/fr/conf.php
index 87f9be290..6193b7a01 100644
--- a/app/i18n/fr/conf.php
+++ b/app/i18n/fr/conf.php
@@ -72,6 +72,10 @@ return array(
),
'profile' => array(
'_' => 'Gestion du profil',
+ 'delete' => array(
+ '_' => 'Suppression du compte',
+ 'warn' => 'Le compte et toutes les données associées vont être supprimées.',
+ ),
'email_persona' => 'Adresse courriel de connexion<br /><small>(pour <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
'password_api' => 'Mot de passe API<br /><small>(ex. : pour applis mobiles)</small>',
'password_form' => 'Mot de passe<br /><small>(pour connexion par formulaire)</small>',
diff --git a/app/i18n/fr/feedback.php b/app/i18n/fr/feedback.php
index e2364a251..99c193d28 100644
--- a/app/i18n/fr/feedback.php
+++ b/app/i18n/fr/feedback.php
@@ -102,6 +102,7 @@ return array(
'_' => 'L’utilisateur %s a été supprimé.',
'error' => 'L’utilisateur %s ne peut pas être supprimé.',
),
+ 'set_registration' => 'Le nombre maximal de comptes a été mis à jour.',
),
'profile' => array(
'error' => 'Votre profil n’a pas pu être mis à jour',
diff --git a/app/i18n/fr/gen.php b/app/i18n/fr/gen.php
index c81e57bf7..67d278be4 100644
--- a/app/i18n/fr/gen.php
+++ b/app/i18n/fr/gen.php
@@ -21,15 +21,27 @@ return array(
'truncate' => 'Supprimer tous les articles',
),
'auth' => array(
+ 'email' => 'Adresse courriel',
'keep_logged_in' => 'Rester connecté <small>(1 mois)</small>',
'login' => 'Connexion',
'login_persona' => 'Connexion avec Persona',
'login_persona_problem' => 'Problème de connexion à Persona ?',
'logout' => 'Déconnexion',
- 'password' => 'Mot de passe',
+ 'password' => array(
+ '_' => 'Mot de passe',
+ 'format' => '<small>7 caractères minimum</small>',
+ ),
+ 'registration' => array(
+ '_' => 'Nouveau compte',
+ 'ask' => 'Créer un compte ?',
+ 'title' => 'Création de compte',
+ ),
'reset' => 'Réinitialisation de l’authentification',
- 'username' => 'Nom d’utilisateur',
- 'username_admin' => 'Nom d’utilisateur administrateur',
+ 'username' => array(
+ '_' => 'Nom d’utilisateur',
+ 'admin' => 'Nom d’utilisateur administrateur',
+ 'format' => '<small>16 caractères alphanumériques maximum</small>',
+ ),
'will_reset' => 'Le système d’authentification va être réinitialisé : un formulaire sera utilisé à la place de Persona.',
),
'date' => array(
diff --git a/app/views/auth/formLogin.phtml b/app/views/auth/formLogin.phtml
index 979e17349..b0083944f 100644
--- a/app/views/auth/formLogin.phtml
+++ b/app/views/auth/formLogin.phtml
@@ -1,6 +1,10 @@
<div class="prompt">
<h1><?php echo _t('gen.auth.login'); ?></h1>
+ <?php if (!max_registrations_reached()) { ?>
+ <a href="<?php echo _url('auth', 'register'); ?>"><?php echo _t('gen.auth.registration.ask'); ?></a>
+ <?php } ?>
+
<form id="crypto-form" method="post" action="<?php echo _url('auth', 'login'); ?>">
<div>
<label for="username"><?php echo _t('gen.auth.username'); ?></label>
diff --git a/app/views/auth/personaLogin.phtml b/app/views/auth/personaLogin.phtml
index 545ed2eac..c6d738bf6 100644
--- a/app/views/auth/personaLogin.phtml
+++ b/app/views/auth/personaLogin.phtml
@@ -2,6 +2,10 @@
<div class="prompt">
<h1><?php echo _t('gen.auth.login'); ?></h1>
+ <?php if (!max_registrations_reached()) { ?>
+ <a href="<?php echo _url('auth', 'register'); ?>"><?php echo _t('gen.auth.registration.ask'); ?></a>
+ <?php } ?>
+
<p>
<a class="signin btn btn-important" href="<?php echo _url('auth', 'login'); ?>">
<?php echo _i('login'); ?> <?php echo _t('gen.auth.login_persona'); ?>
diff --git a/app/views/auth/register.phtml b/app/views/auth/register.phtml
new file mode 100644
index 000000000..306679601
--- /dev/null
+++ b/app/views/auth/register.phtml
@@ -0,0 +1,38 @@
+<div class="prompt">
+ <h1><?php echo _t('gen.auth.registration'); ?></h1>
+
+ <form method="post" action="<?php echo _url('user', 'create'); ?>">
+ <div>
+ <label class="group-name" for="new_user_name"><?php echo _t('gen.auth.username'), '<br />', _i('help'), ' ', _t('gen.auth.username.format'); ?></label>
+ <input id="new_user_name" name="new_user_name" type="text" size="16" required="required" maxlength="16" autocomplete="off" pattern="[0-9a-zA-Z]{1,16}" />
+ </div>
+
+ <div>
+ <label class="group-name" for="new_user_passwordPlain"><?php echo _t('gen.auth.password'), '<br />', _i('help'), ' ', _t('gen.auth.password.format'); ?></label>
+ <div class="stick">
+ <input type="password" id="new_user_passwordPlain" name="new_user_passwordPlain" required="required" autocomplete="off" pattern=".{7,}" />
+ <a class="btn toggle-password" data-toggle="new_user_passwordPlain"><?php echo _i('key'); ?></a>
+ </div>
+ <noscript><b><?php echo _t('gen.js.should_be_activated'); ?></b></noscript>
+ </div>
+
+ <div>
+ <label class="group-name" for="new_user_email"><?php echo _t('gen.auth.email'); ?></label>
+ <input type="email" id="new_user_email" name="new_user_email" class="extend" required="required" autocomplete="off" />
+ </div>
+
+ <div>
+ <?php
+ $redirect_url = urlencode(Minz_Url::display(
+ array('c' => 'index', 'a' => 'index'),
+ 'php', true
+ ));
+ ?>
+ <input type="hidden" name="r" value="<?php echo $redirect_url; ?>" />
+ <button type="submit" class="btn btn-important"><?php echo _t('gen.action.create'); ?></button>
+ <a class="btn" href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.cancel'); ?></a>
+ </div>
+ </form>
+
+ <p><a href="<?php echo _url('index', 'about'); ?>"><?php echo _t('gen.freshrss.about'); ?></a></p>
+</div>
diff --git a/app/views/auth/reset.phtml b/app/views/auth/reset.phtml
index 6e9816ad3..9c820c7c8 100644
--- a/app/views/auth/reset.phtml
+++ b/app/views/auth/reset.phtml
@@ -16,7 +16,7 @@
</p>
<div>
- <label for="username"><?php echo _t('gen.auth.username_admin'); ?></label>
+ <label for="username"><?php echo _t('gen.auth.username.admin'); ?></label>
<input type="text" id="username" name="username" size="16" required="required" maxlength="16" pattern="[0-9a-zA-Z]{1,16}" autofocus="autofocus" />
</div>
<div>
diff --git a/app/views/user/manage.phtml b/app/views/user/manage.phtml
index fe1b6618b..3d3bc3ddf 100644
--- a/app/views/user/manage.phtml
+++ b/app/views/user/manage.phtml
@@ -3,6 +3,34 @@
<div class="post">
<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a>
+ <form method="post" action="<?php echo _url('user', 'setRegistration'); ?>">
+ <legend><?php echo _t('admin.user.registration.allow'); ?></legend>
+
+ <div class="form-group">
+ <label class="group-name" for="max-registrations"><?php echo _t('admin.user.registration.number'); ?></label>
+ <div class="group-controls">
+ <input type="number" id="max-registrations" name="max-registrations" value="<?php echo FreshRSS_Context::$system_conf->limits['max_registrations']; ?>" min="0" data-leave-validation="<?php echo FreshRSS_Context::$system_conf->limits['max_registrations']; ?>"/>
+ <?php echo _i('help'); ?> <?php echo _t('admin.user.registration.help'); ?>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <div class="group-controls">
+ <?php
+ $number = count(listUsers());
+ echo _t($number > 1 ? 'admin.user.numbers' : 'admin.user.number', $number);
+ ?>
+ </div>
+ </div>
+
+ <div class="form-group form-actions">
+ <div class="group-controls">
+ <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button>
+ <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button>
+ </div>
+ </div>
+ </form>
+
<form method="post" action="<?php echo _url('user', 'create'); ?>">
<legend><?php echo _t('admin.user.create'); ?></legend>
diff --git a/app/views/user/profile.phtml b/app/views/user/profile.phtml
index c44202edd..7ae2c7ede 100644
--- a/app/views/user/profile.phtml
+++ b/app/views/user/profile.phtml
@@ -18,11 +18,11 @@
</div>
<div class="form-group">
- <label class="group-name" for="passwordPlain"><?php echo _t('conf.profile.password_form'); ?></label>
+ <label class="group-name" for="newPasswordPlain"><?php echo _t('conf.profile.password_form'); ?></label>
<div class="group-controls">
<div class="stick">
- <input type="password" id="passwordPlain" name="passwordPlain" autocomplete="off" pattern=".{7,}" <?php echo cryptAvailable() ? '' : 'disabled="disabled" '; ?>/>
- <a class="btn toggle-password" data-toggle="passwordPlain"><?php echo _i('key'); ?></a>
+ <input type="password" id="newPasswordPlain" name="newPasswordPlain" autocomplete="off" pattern=".{7,}" <?php echo cryptAvailable() ? '' : 'disabled="disabled" '; ?>/>
+ <a class="btn toggle-password" data-toggle="newPasswordPlain"><?php echo _i('key'); ?></a>
</div>
<?php echo _i('help'); ?> <?php echo _t('conf.profile.password_format'); ?>
<noscript><b><?php echo _t('gen.js.should_be_activated'); ?></b></noscript>
@@ -57,4 +57,35 @@
</div>
</div>
</form>
+
+ <?php if (!FreshRSS_Auth::hasAccess('admin')) { ?>
+ <form id="crypto-form" method="post" action="<?php echo _url('user', 'delete'); ?>">
+ <legend><?php echo _t('conf.profile.delete'); ?></legend>
+
+ <p class="alert alert-warn"><span class="alert-head"><?php echo _t('gen.short.attention'); ?></span> <?php echo _t('conf.profile.delete.warn'); ?></p>
+
+ <div class="form-group">
+ <label class="group-name" for="passwordPlain"><?php echo _t('gen.auth.password'); ?></label>
+ <div class="group-controls">
+ <input type="password" id="passwordPlain" required="required" />
+ <input type="hidden" id="challenge" name="challenge" /><br />
+ <noscript><strong><?php echo _t('gen.js.should_be_activated'); ?></strong></noscript>
+ </div>
+ </div>
+
+ <div class="form-group form-actions">
+ <div class="group-controls">
+ <?php
+ $redirect_url = urlencode(Minz_Url::display(
+ array('c' => 'user', 'a' => 'profile'),
+ 'php', true
+ ));
+ ?>
+ <input type="hidden" name="r" value="<?php echo $redirect_url; ?>" />
+ <input type="hidden" name="username" id="username" value="<?php echo Minz_Session::param('currentUser', '_'); ?>" />
+ <button type="submit" class="btn btn-attention confirm"><?php echo _t('gen.action.remove'); ?></button>
+ </div>
+ </div>
+ </form>
+ <?php } ?>
</div>
diff --git a/data/config.default.php b/data/config.default.php
index 6013b13b8..5db933ff8 100644
--- a/data/config.default.php
+++ b/data/config.default.php
@@ -77,6 +77,10 @@ return array(
# Max number of categories for a user.
'max_categories' => 16384,
+ # Max number of accounts that anonymous users can create
+ # 0 for an unlimited number of accounts
+ # 1 is to not allow user registrations (1 is corresponding to the admin account)
+ 'max_registrations' => 1,
),
# Options used by cURL when making HTTP requests, e.g. when the SimplePie library retrieves feeds.
diff --git a/lib/lib_rss.php b/lib/lib_rss.php
index 0118e0f46..c99e2c7e8 100644
--- a/lib/lib_rss.php
+++ b/lib/lib_rss.php
@@ -267,6 +267,22 @@ function listUsers() {
/**
+ * Return if the maximum number of registrations has been reached.
+ *
+ * Note a max_regstrations of 0 means there is no limit.
+ *
+ * @return true if number of users >= max registrations, false else.
+ */
+function max_registrations_reached() {
+ $system_conf = Minz_Configuration::get('system');
+ $limit_registrations = $system_conf->limits['max_registrations'];
+ $number_accounts = count(listUsers());
+
+ return $limit_registrations > 0 && $number_accounts >= $limit_registrations;
+}
+
+
+/**
* Register and return the configuration for a given user.
*
* Note this function has been created to generate temporary configuration