1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-29 18:52:22 +01:00

Merge branch 'user-confirmation' into dev

This commit is contained in:
Sylvain 2020-01-07 10:34:25 +01:00
commit 3c3e806a29
19 changed files with 133 additions and 27 deletions

View File

@ -6,6 +6,7 @@
- An event can be cancelled, if reservation cancellation is enabled
- Ability to import iCalendar agendas in the public calendar, through URLs to ICS files (RFC 5545)
- Ability to configure the duration of a reservation slot, using `SLOT_DURATION`. Previously, only 60 minutes slots were allowed
- Ability to force the email validation when a new user registers. This is optionally configured with `USER_CONFIRMATION_NEEDED_TO_SIGN_IN`
- Display the scheduled events in the admin calendar, depending on `EVENTS_IN_CALENDAR` configuration.
- Display indications on required fields in new administrator form
- Configuration of phone number in members registration forms: can be required or optional, depending on `PHONE_REQUIRED` configuration
@ -32,6 +33,7 @@
- [TODO DEPLOY] add the `SLOT_DURATION` environment variable (see [doc/environment.md](doc/environment.md#SLOT_DURATION) for configuration details)
- [TODO DEPLOY] add the `PHONE_REQUIRED` environment variable (see [doc/environment.md](doc/environment.md#PHONE_REQUIRED) for configuration details)
- [TODO DEPLOY] add the `EVENTS_IN_CALENDAR` environment variable (see [doc/environment.md](doc/environment.md#EVENTS_IN_CALENDAR) for configuration details)
- [TODO DEPLOY] add the `USER_CONFIRMATION_NEEDED_TO_SIGN_IN` environment variable (see [doc/environment.md](doc/environment.md#USER_CONFIRMATION_NEEDED_TO_SIGN_IN) for configuration details)
- [TODO DEPLOY] -> (only dev) `bundle install && yarn install`
- [TODO DEPLOY] `rake db:migrate`

View File

@ -92,6 +92,8 @@ angular.module('application', ['ngCookies', 'ngResource', 'ngSanitize', 'ui.rout
$rootScope.eventsInCalendar = Fablab.eventsInCalendar;
// Global config: machine/space slot duration
$rootScope.slotDuration = Fablab.slotDuration;
// Global config: if true, user must confirm his email to sign in
$rootScope.userConfirmationNeededToSignIn = Fablab.userConfirmationNeededToSignIn;
// Global function to allow the user to navigate to the previous screen (ie. $state).
// If no previous $state were recorded, navigate to the home page

View File

@ -169,7 +169,12 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
}]
}).result['finally'](null).then(function (user) {
// when the account was created successfully, set the session to the newly created account
$scope.setCurrentUser(user);
if(Fablab.userConfirmationNeededToSignIn) {
Auth._currentUser = null;
growl.info(_t('app.public.common.you_will_receive_confirmation_instructions_by_email_detailed'));
} else {
$scope.setCurrentUser(user);
}
});
<% end %>
};
@ -375,7 +380,7 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
console.error(`Authentication failed: ${JSON.stringify(error)}`);
$scope.alerts = [];
return $scope.alerts.push({
msg: _t('app.public.common.wrong_email_or_password'),
msg: error.data.error,
type: 'danger'
});
});
@ -388,6 +393,11 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
return $uibModalInstance.dismiss('signup');
};
$scope.openConfirmationNewModal = function(e) {
e.preventDefault();
return $uibModalInstance.dismiss('confirmationNew');
};
return $scope.openResetPassword = function (e) {
e.preventDefault();
return $uibModalInstance.dismiss('resetPassword');
@ -425,6 +435,24 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
};
}]
}).result['finally'](null).then(function () { growl.info(_t('app.public.common.you_will_receive_in_a_moment_an_email_with_instructions_to_reset_your_password')); });
} else if (reason === 'confirmationNew') {
// open the 'reset password' modal
return $uibModal.open({
templateUrl: '<%= asset_path "shared/ConfirmationNewModal.html" %>',
size: 'sm',
controller: ['$scope', '$uibModalInstance', '$http', function ($scope, $uibModalInstance, $http) {
$scope.user = { email: '' };
return $scope.submitConfirmationNewForm = function () {
$scope.alerts = [];
return $http.post('/users/confirmation.json', { user: $scope.user }).then(function () { $uibModalInstance.close(); }).catch(function (res) {
$scope.alerts.push({
msg: res.data.errors.email[0],
type: 'danger'
});
});
};
}]
}).result['finally'](null).then(function () { growl.info(_t('app.public.common.you_will_receive_confirmation_instructions_by_email_detailed')); });
}
});
// otherwise the user just closed the modal

View File

@ -1,17 +1,18 @@
'use strict';
Application.Services.factory('AuthService', ['Session', 'CSRF', function (Session, CSRF) {
return {
isAuthenticated () {
return (Session.currentUser != null) && (Session.currentUser.id != null);
},
let service = {};
isAuthorized (authorizedRoles) {
if (!angular.isArray(authorizedRoles)) {
authorizedRoles = [authorizedRoles];
}
return this.isAuthenticated() && (authorizedRoles.indexOf(Session.currentUser.role) !== -1);
}
service.isAuthenticated = function() {
return (Session.currentUser != null) && (Session.currentUser.id != null);
};
service.isAuthorized = function(authorizedRoles) {
if (!angular.isArray(authorizedRoles)) {
authorizedRoles = [authorizedRoles];
}
return service.isAuthenticated() && (authorizedRoles.indexOf(Session.currentUser.role) !== -1);
};
return service;
}]);

View File

@ -0,0 +1,35 @@
<div>
<div class="modal-header">
<img ng-src="{{logoBlack.custom_asset_file_attributes.attachment_url}}" alt="{{logo.custom_asset_file_attributes.attachment}}" class="modal-logo"/>
<h1 translate>{{ 'app.public.common.confirm_my_account' }}</h1>
</div>
<div class="modal-body">
<uib-alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)">{{alert.msg}}</uib-alert>
<div class="panel panel-default bg-light">
<div class="panel-body">
<p translate>{{ 'app.public.common.you_will_receive_confirmation_instructions_by_email' }}</p>
<form name="confirmationNewForm" class="form-horizontal" ng-keydown="confirmationNewForm.$valid && $event.which == 13 && submitConfirmationNewForm()">
<div class="form-group" ng-class="{'has-error': emailError}">
<div class="col-sm-12">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-envelope"></i></span>
<input type="email"
name="email"
class="form-control"
ng-model="user.email"
required="required"
ng-blur="emailError = !!confirmationNewForm.email.$error.email"
ng-focus="emailError = false"
placeholder="{{ 'app.public.common.your_email_address' | translate }}" />
</div>
</div>
</div>
</form>
</div>
<div class="panel-footer no-padder">
<button class="btn btn-valid btn-warning btn-block p-l btn-lg text-u-c r-b" ng-click="submitConfirmationNewForm()" ng-disabled="confirmationNewForm.$invalid" translate>{{ 'app.shared.buttons.confirm' }}</button>
</div>
</div>
</div>
</div>

View File

@ -42,6 +42,9 @@
ng-minlength="8"/>
</div>
<a href="#" ng-click="openResetPassword($event)" class="text-xs">{{ 'app.public.common.password_forgotten' | translate }}</a>
<span ng-if="userConfirmationNeededToSignIn">
<br><a href="#" ng-click="openConfirmationNewModal($event)" class="text-xs">{{ 'app.public.common.confirm_my_account' | translate }}</a>
</span>
<div class="alert alert-warning m-t-sm m-b-none text-xs p-sm" ng-show='isCapsLockOn' role="alert">
<i class="fa fa-warning"></i>
{{ 'app.public.common.caps_lock_is_on' | translate }}

View File

@ -289,7 +289,7 @@ class User < ActiveRecord::Base
end
def confirmation_required?
false
Rails.application.secrets.user_confirmation_needed_to_sign_in ? super : false
end
private

View File

@ -60,6 +60,7 @@
<% if RecaptchaService.recaptcha_enabled? %>
Fablab.recaptchaSiteKey = "<%= RecaptchaService.site_key %>";
<% end %>
Fablab.userConfirmationNeededToSignIn = ('<%= Rails.application.secrets.user_confirmation_needed_to_sign_in %>' === 'true');
</script>
<%= stylesheet_link_tag 'application', media: 'all' %>

View File

@ -22,6 +22,8 @@ FABLAB_WITHOUT_ONLINE_PAYMENT: 'false'
FABLAB_WITHOUT_INVOICES: 'false'
PHONE_REQUIRED: 'true'
USER_CONFIRMATION_NEEDED_TO_SIGN_IN: 'false'
EVENTS_IN_CALENDAR: 'false'
SLOT_DURATION: '60'
@ -100,3 +102,4 @@ MAX_IMPORT_SIZE: '5242880'
MAX_IMAGE_SIZE: '10485760'
# 20971520 = 20 megabytes
MAX_CAO_SIZE: '20971520'

View File

@ -98,6 +98,7 @@ en:
used_for_reservation: "This data will be used in case of change on one of your bookings"
used_for_profile: "This data will only be displayed on your profile"
public_profile: "You will have a public profile and other users will be able to associate you in their projects"
you_will_receive_confirmation_instructions_by_email_detailed: "You will receive an email with instructions about how to confirm your account in a few minutes."
# password modification modal
change_your_password: "Change your password"
@ -107,11 +108,16 @@ en:
# connection modal
connection: "Connection"
password_forgotten: "Forgotten password?"
confirm_my_account: "Confirm my e-mail"
not_registered_to_the_fablab: "Not registered to the Fablab?"
create_an_account: "Create an account"
wrong_email_or_password: "Wrong e-mail or password."
caps_lock_is_on: "Caps lock key is on."
# confirmation modal
you_will_receive_confirmation_instructions_by_email: You will receive confirmation instructions by email.
# forgotten password modal
your_email_address_is_unknown: "Your e-mail address is unknown."
you_will_receive_in_a_moment_an_email_with_instructions_to_reset_your_password: "You will receive in a moment, an e-mail with instructions to reset your password."

View File

@ -98,6 +98,7 @@ es:
used_for_reservation: "This data will be used in case of change on one of your bookings"
used_for_profile: "This data will only be displayed on your profile"
public_profile: "You will have a public profile and other users will be able to associate you in their projects"
you_will_receive_confirmation_instructions_by_email_detailed: "Recibirá un correo electrónico con instrucciones sobre cómo confirmar su cuenta en unos minutos."
# password modification modal
change_your_password: "Cambiar contraseña"
@ -107,11 +108,15 @@ es:
# connection modal
connection: "Conexión"
password_forgotten: "¿Ha olvidado su contraseña?"
confirm_my_account: "Confirmar mi E-mail"
not_registered_to_the_fablab: "¿No está registrado en el fatlab aún?"
create_an_account: "Crear una cuenta"
wrong_email_or_password: "E-mail o contraseña incorrecta."
caps_lock_is_on: "Las mayusculas están activadas."
# confirmation modal
you_will_receive_confirmation_instructions_by_email: Recibirá las instrucciones de confirmación por email.
# forgotten password modal
your_email_address_is_unknown: "Se desconoce su email."
you_will_receive_in_a_moment_an_email_with_instructions_to_reset_your_password: "En un momento recibirá las instrucciones para restablecer su contraseña en su mail."

View File

@ -98,6 +98,7 @@ fr:
used_for_reservation: "Cette donnée sera utilisée en cas de changement sur une de vos réservations"
used_for_profile: "Cette donnée sera seulement affichée sur votre profil"
public_profile: "Vous aurez un profil public et les autres utilisateurs pourront vous associer à leurs projets"
you_will_receive_confirmation_instructions_by_email_detailed: "Vous allez recevoir dans quelques minutes un email comportant des instructions pour confirmer votre compte."
# fenêtre de changement de mot de passe
change_your_password: "Modifier votre mot de passe"
@ -107,11 +108,15 @@ fr:
# fenêtre de connexion
connection: "Connexion"
password_forgotten: "Mot de passe oublié ?"
confirm_my_account: "Confirmer mon adresse de courriel"
not_registered_to_the_fablab: "Vous n'êtes pas inscrit au FAB LAB ?"
create_an_account: "Créer un compte"
wrong_email_or_password: "Adresse courriel ou mot de passe incorrect."
caps_lock_is_on: "La touche de verrouillage des majuscules est activée."
# fenêtre d'envoi des instructions de confirmation
you_will_receive_confirmation_instructions_by_email: Vous recevrez les instructions de confirmation par email.
# mot de passe oublié
your_email_address_is_unknown: "Votre adresse de courriel est inconnue."
you_will_receive_in_a_moment_an_email_with_instructions_to_reset_your_password: "Vous allez recevoir sous quelques minutes un courriel vous indiquant comment réinitialiser votre mot de passe."

View File

@ -98,6 +98,7 @@ pt:
used_for_reservation: "This data will be used in case of change on one of your bookings"
used_for_profile: "This data will only be displayed on your profile"
public_profile: "You will have a public profile and other users will be able to associate you in their projects"
you_will_receive_confirmation_instructions_by_email_detailed: "Você receberá um email com instruções sobre como confirmar sua conta em alguns minutos."
# password modification modal
change_your_password: "Mudar sua senha"
@ -107,11 +108,15 @@ pt:
# connection modal
connection: "Login"
password_forgotten: "Esqueceu sua senha?"
confirm_my_account: "Confirmar sua conta"
not_registered_to_the_fablab: "Ainda não registrado no Fablab?"
create_an_account: "Criar conta"
wrong_email_or_password: "E-mail ou senha incorretos."
caps_lock_is_on: "A tecla Caps Lock está ativada."
# confirmation modal
you_will_receive_confirmation_instructions_by_email: Você receberá instruções de confirmação por e-mail.
# forgotten password modal
your_email_address_is_unknown: "Seu e-mail não está cadastrado."
you_will_receive_in_a_moment_an_email_with_instructions_to_reset_your_password: "Você irá receber um e-mail com as instruções para resetar sua senha."

View File

@ -15,7 +15,7 @@ en:
not_found_in_database: "Invalid email or password."
timeout: "Your session expired. Please sign in again to continue."
unauthenticated: "You need to sign in or sign up before continuing."
unconfirmed: "You have to confirm your account before continuing."
unconfirmed: "You have to confirm your account before continuing. Please click on the link below the form."
mailer:
confirmation_instructions:
action:
@ -54,10 +54,10 @@ en:
unlocked: "Your account has been unlocked successfully. Please sign in to continue."
errors:
messages:
already_confirmed: "was already confirmed, please try signing in"
already_confirmed: "This email was already confirmed, please try signing in."
confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one"
expired: "has expired, please request a new one"
not_found: "not found"
not_found: "This email was not found"
not_locked: "was not locked"
not_saved:
one: "1 error prohibited this %{resource} from being saved:"

View File

@ -15,7 +15,7 @@ es:
not_found_in_database: "mail o contraseña inválidos."
timeout: "Su sesión ha expirado. Por favor, inicie sesión de nuevo."
unauthenticated: "Necesita iniciar sesión o registrarse antes de contiunar."
unconfirmed: "Debe confirmar su cuenta antes de continuar."
unconfirmed: "Debe confirmar su cuenta antes de continuar. Por favor haga clic en el enlace abajo del formulario."
mailer:
confirmation_instructions:
action:
@ -54,10 +54,10 @@ es:
unlocked: "Tu cuenta se ha desbloqueado con éxito. Por favor inicie sesión para continuar."
errors:
messages:
already_confirmed: "Ya se confirmó, intente iniciar sesión"
already_confirmed: "Ya se confirmó, intente iniciar sesión."
confirmation_period_expired: "Necesita ser confirmado dentro de %{period}, por favor, solicite uno nuevo"
expired: "ha expirado, por favor, solicite uno nuevo"
not_found: "no encontrado"
not_found: "Este correo no esta associado con ninguna cuenta."
not_locked: "no estaba bloqueado"
not_saved:
one: "un error prohibió que %{resource} fuese guardado:"

View File

@ -9,13 +9,13 @@ fr:
failure:
already_authenticated: "Vous êtes déjà connecté(e)."
inactive: "Votre compte nest pas encore activé."
invalid: "E-mail ou mot de passe incorrect."
invalid: "Adresse courriel ou mot de passe incorrect."
invalid_token: "Jeton d'authentification incorrect."
locked: "Votre compte est verrouillé."
not_found_in_database: "E-mail ou mot de passe incorrect."
timeout: "Votre session est périmée, veuillez vous reconnecter pour continuer."
unauthenticated: "Vous devez vous connecter ou vous enregistrer pour continuer."
unconfirmed: "Vous devez confirmer votre compte par e-mail."
unconfirmed: "Vous devez confirmer votre adresse de courriel pour pouvoir vous connecter. Veuillez cliquer sur le lien en dessous du formulaire."
mailer:
confirmation_instructions:
action: "Confirmer mon e-mail !"
@ -54,10 +54,10 @@ fr:
unlocked: "Votre compte a été débloqué avec succès. Veuillez vous connecter."
errors:
messages:
already_confirmed: "a déjà été confirmé(e)"
already_confirmed: "Cette adresse de courriel a déjà été confirmée."
confirmation_period_expired: "doit être confirmé(e) en %{period}, veuillez en demander un(e) autre"
expired: "est périmé, veuillez en demander un autre"
not_found: "na pas été trouvé(e)"
not_found: "Cette adresse de courriel n'est associée à aucun compte."
not_locked: "nétait pas verrouillé(e)"
not_saved:
one: "une erreur a empêché ce (ou cette) %{resource} dêtre enregistré(e) :"

View File

@ -15,7 +15,7 @@ pt:
not_found_in_database: "Email ou senha inválidos."
timeout: "Sua sessão expirou, faça login novamente para continuar."
unauthenticated: "Você precisa fazer login ou se registrar, antes de continuar."
unconfirmed: "Você precisa confirmar sua conta, antes de continuar."
unconfirmed: "Você precisa confirmar sua conta, antes de continuar. Por favor, clique no link abaixo do formulário."
mailer:
confirmation_instructions:
action:
@ -54,10 +54,10 @@ pt:
unlocked: "Sua conta foi desbloqueada com sucesso. Faça login para continuar."
errors:
messages:
already_confirmed: "já foi confirmado, por favor, efetue login"
already_confirmed: "Este email já foi confirmado."
confirmation_period_expired: "deve ser confirmada dentro de %{period}, por favor solicite uma nova"
expired: "expirado, por favor solicite uma nova"
not_found: "não encontrado"
not_found: "Este email não está associado a nenhuma conta."
not_locked: "não encontra-se bloqueada"
not_saved:
one: "1 erro impediu a gravação de %{resource} :"

View File

@ -21,6 +21,7 @@ development:
fablab_without_online_payments: <%= ENV["FABLAB_WITHOUT_ONLINE_PAYMENT"] %>
fablab_without_invoices: <%= ENV["FABLAB_WITHOUT_INVOICES"] %>
phone_required: <%= ENV["PHONE_REQUIRED"] %>
user_confirmation_needed_to_sign_in: <%= ENV["USER_CONFIRMATION_NEEDED_TO_SIGN_IN"] %>
events_in_calendar: <%= ENV["EVENTS_IN_CALENDAR"] %>
slot_duration: <%= ENV["SLOT_DURATION"] %>
default_host: <%= ENV["DEFAULT_HOST"] %>
@ -66,6 +67,7 @@ test:
fablab_without_online_payments: false
fablab_without_invoices: false
phone_required: true
user_confirmation_needed_to_sign_in: <%= ENV["USER_CONFIRMATION_NEEDED_TO_SIGN_IN"] %>
events_in_calendar: false
slot_duration: 60
default_host: <%= ENV["DEFAULT_HOST"] %>
@ -111,6 +113,7 @@ staging:
fablab_without_online_payments: <%= ENV["FABLAB_WITHOUT_ONLINE_PAYMENT"] %>
fablab_without_invoices: <%= ENV["FABLAB_WITHOUT_INVOICES"] %>
phone_required: <%= ENV["PHONE_REQUIRED"] %>
user_confirmation_needed_to_sign_in: <%= ENV["USER_CONFIRMATION_NEEDED_TO_SIGN_IN"] %>
events_in_calendar: <%= ENV["EVENTS_IN_CALENDAR"] %>
slot_duration: <%= ENV["SLOT_DURATION"] %>
default_host: <%= ENV["DEFAULT_HOST"] %>
@ -168,6 +171,7 @@ production:
fablab_without_online_payments: <%= ENV["FABLAB_WITHOUT_ONLINE_PAYMENT"] %>
fablab_without_invoices: <%= ENV["FABLAB_WITHOUT_INVOICES"] %>
phone_required: <%= ENV["PHONE_REQUIRED"] %>
user_confirmation_needed_to_sign_in: <%= ENV["USER_CONFIRMATION_NEEDED_TO_SIGN_IN"] %>
events_in_calendar: <%= ENV["EVENTS_IN_CALENDAR"] %>
slot_duration: <%= ENV["SLOT_DURATION"] %>
default_host: <%= ENV["DEFAULT_HOST"] %>

View File

@ -107,6 +107,12 @@ This is useful if you have your own invoicing system and you want to prevent Fab
PHONE_REQUIRED
If set to 'false' the phone number won't be required to register a new user on the software.
<a name="USER_CONFIRMATION_NEEDED_TO_SIGN_IN"></a>
USER_CONFIRMATION_NEEDED_TO_SIGN_IN
If set to 'true' the users will need to confirm their email address to be able to sign in.
Set to 'false' if you don't want this behaviour.
<a name="EVENTS_IN_CALENDAR"></a>
EVENTS_IN_CALENDAR