Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fc signup form #87

Merged
merged 7 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions app/commands/decidim/create_omniauth_registration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# frozen_string_literal: true

module Decidim
# A command with all the business logic to create a user from omniauth
class CreateOmniauthRegistration < Decidim::Command
# Public: Initializes the command.
#
# form - A form object with the params.
def initialize(form, verified_email = nil)
@form = form
@verified_email = verified_email
end

# Executes the command. Broadcasts these events:
#
# - :ok when everything is valid.
# - :invalid if the form wasn't valid and we couldn't proceed.
#
# Returns nothing.
def call
verify_oauth_signature!

begin
if existing_identity
user = existing_identity.user
verify_user_confirmed(user)

return broadcast(:ok, user)
end
return broadcast(:invalid) if form.invalid?

transaction do
create_or_find_user
@identity = create_identity
end
trigger_omniauth_registration

broadcast(:ok, @user)
rescue ActiveRecord::RecordInvalid => e
broadcast(:error, e.record)
end
end

private

attr_reader :form, :verified_email

def create_or_find_user
generated_password = SecureRandom.hex

@user = User.find_or_initialize_by(
email: verified_email,
organization: organization
)

if @user.persisted?
# If user has left the account unconfirmed and later on decides to sign
# in with omniauth with an already verified account, the account needs
# to be marked confirmed.
@user.skip_confirmation! if [email protected]? && @user.email == verified_email
else
@user.email = (verified_email || form.email)
@user.name = form.name
@user.nickname = form.normalized_nickname
@user.newsletter_notifications_at = nil
@user.password = generated_password
@user.password_confirmation = generated_password
if form.avatar_url.present?
url = URI.parse(form.avatar_url)
filename = File.basename(url.path)
file = url.open
@user.avatar.attach(io: file, filename: filename)
end
@user.extended_data = extended_data
@user.skip_confirmation! if verified_email
end

@user.tos_agreement = "1"
@user.save!
end

def create_identity
@user.identities.create!(
provider: form.provider,
uid: form.uid,
organization: organization
)
end

def organization
@form.current_organization
end

def existing_identity
@existing_identity ||= Identity.find_by(
user: organization.users,
provider: form.provider,
uid: form.uid
)
end

def verify_user_confirmed(user)
return true if user.confirmed?
return false if user.email != verified_email

user.skip_confirmation!
user.save!
end

def verify_oauth_signature!
raise InvalidOauthSignature, "Invalid oauth signature: #{form.oauth_signature}" unless signature_valid?
end

def signature_valid?
signature = OmniauthRegistrationForm.create_signature(form.provider, form.uid)
form.oauth_signature == signature
end

def trigger_omniauth_registration
ActiveSupport::Notifications.publish(
"decidim.user.omniauth_registration",
user_id: @user.id,
identity_id: @identity.id,
provider: form.provider,
uid: form.uid,
email: form.email,
name: form.name,
nickname: form.normalized_nickname,
avatar_url: form.avatar_url,
raw_data: form.raw_data
)
end

def extended_data
{
birth_date: form.birth_date,
address: form.address,
postal_code: form.postal_code,
city: form.city,
certification: form&.certification
}
end
end

class InvalidOauthSignature < StandardError
end
end
21 changes: 1 addition & 20 deletions app/packs/entrypoints/application.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,2 @@
/* eslint no-console:0 */
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/packs and only use these pack files to reference
// that code so it'll be compiled.
//
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
// layout file, like app/views/layouts/application.html.erb
import "src/signup_form.js"

// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)

// Activate Active Storage
// import * as ActiveStorage from "@rails/activestorage"
// ActiveStorage.start()

import "src/signup_form.js"
3 changes: 3 additions & 0 deletions app/packs/src/omniauth_registration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
$(() => {
$("#new_user label[for='user_birth_date'] select").wrapAll('<div class="select-date-container">');
});
48 changes: 28 additions & 20 deletions app/packs/src/signup_form.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
$(document).ready(() => {
$("label[for='registration_user_birth_date'] select").wrapAll('<div class="select-date-container">');
// Omniauth registration form
$("#new_user label[for='user_birth_date'] select").wrapAll('<div class="select-date-container">');

// Sélection des éléments du DOM
const passwordInput = document.getElementById('registration_user_password');
Expand All @@ -10,7 +12,9 @@ $(document).ready(() => {
const fieldElements = document.querySelectorAll('.field');

// Masquer la classe "user-nickname"
userNicknameField.style.display = 'none';
if (userNicknameField !== null) {
userNicknameField.style.display = 'none';
}

// Masquer la classe "field" contenant la classe "registration_user_password_confirmation"
fieldElements.forEach(field => {
Expand All @@ -21,16 +25,18 @@ $(document).ready(() => {
});

// // Écouteur d'événement sur le champ du mot de passe
passwordInput.addEventListener('input', function() {
// If there is a form-error behind the password field, remove it
const passwordField = passwordInput.parentElement;
const passwordError = passwordField.querySelector('.form-error');
if (passwordError !== null) {
changeMessage(passwordError)
}
if (passwordInput !== null) {
passwordInput.addEventListener('input', function() {
// If there is a form-error behind the password field, remove it
const passwordField = passwordInput.parentElement;
const passwordError = passwordField.querySelector('.form-error');
if (passwordError !== null) {
changeMessage(passwordError)
}

confirmPasswordInput.value = passwordInput.value;
});
confirmPasswordInput.value = passwordInput.value;
});
}

function changeMessage(passwordError) {
const password = passwordInput.value;
Expand All @@ -42,16 +48,18 @@ $(document).ready(() => {
}

// Génération automatique du surnom à partir du champ du nom
userNameInput.addEventListener('input', function() {
const userName = userNameInput.value.toLowerCase().replace(/\s/g, '_').replace(/[^\w\s]/gi, ''); // Remplacement des espaces par des underscores, suppression des caractères spéciaux et mise en minuscules
const randomNum = Math.floor(100000 + Math.random() * 900000); // Génération d'un nombre aléatoire à 6 chiffres
const generatedNickname = userName + '_' + randomNum;
if (userNameInput !== null) {
userNameInput.addEventListener('input', function() {
const userName = userNameInput.value.toLowerCase().replace(/\s/g, '_').replace(/[^\w\s]/gi, ''); // Remplacement des espaces par des underscores, suppression des caractères spéciaux et mise en minuscules
const randomNum = Math.floor(100000 + Math.random() * 900000); // Génération d'un nombre aléatoire à 6 chiffres
const generatedNickname = userName + '_' + randomNum;

// Remplissage automatique du champ du surnom
userNicknameInput.value = generatedNickname.substr(0, 20); // Limite le surnom à 20 caractères
// Remplissage automatique du champ du surnom
userNicknameInput.value = generatedNickname.substr(0, 20); // Limite le surnom à 20 caractères

// Cacher les champs générés automatiquement
confirmPasswordInput.style.display = 'none'; // Cacher le champ de confirmation du mot de passe
userNicknameInput.style.display = 'none'; // Cacher le champ du surnom
});
// Cacher les champs générés automatiquement
confirmPasswordInput.style.display = 'none'; // Cacher le champ de confirmation du mot de passe
userNicknameInput.style.display = 'none'; // Cacher le champ du surnom
});
}
})
4 changes: 2 additions & 2 deletions app/packs/stylesheets/signup_form.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#register-form {
#register-form, #new_user {
.select-date-container {
display: flex;
flex-direction: row;
Expand All @@ -10,4 +10,4 @@
.select-date-container .columns {
padding-right: 1.4rem;
}
}
}
90 changes: 90 additions & 0 deletions app/views/decidim/devise/omniauth_registrations/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<main class="wrapper">
<div class="row collapse">

<% if @form.errors[:minimum_age].present? %>
<div class="row">
<div class="columns large-6 medium-10 medium-centered">
<div class="flash callout alert">
<%= @form.errors[:minimum_age].join %>
</div>
</div>
</div>
<div class="row">
<div class="columns large-6 medium-10 medium-centered">
<br>
<br>
<%= link_to t("decidim.errors.not_found.back_home"), root_path, class: "button hollow expanded" %>
</div>
</div>
<% else %>
<div class="row collapse">
<div class="columns large-8 large-centered text-center page-title">
<h1><%= t(".sign_up") %></h1>
<p>
<%= t(".subtitle") %>
</p>
</div>
</div>

<div class="row">
<div class="columns large-6 medium-10 medium-centered">
<div class="callout info">
<%== t(".registration_info") %>
</div>

<div class="card">
<div class="card__content">
<%= decidim_form_for(@form, as: resource_name, url: omniauth_registrations_path(resource_name), html: { class: "register-form new_user" }) do |f| %>
<div class="card__content">
<h3><%= t(".personal_data_step") %></h3>

<div class="row">
<div class="columns medium-12">
<%= f.date_select :birth_date, { label: t(".birth_date"), start_year: Date.today.year - 100, end_year: Date.today.year }, { class: "columns medium-3" } %>
</div>
<div class="columns margin-bottom-1">
<small><em><%= t(".birth_date_help") %></em></small>
</div>
</div>

<div class="field">
<%= f.text_field :address, autocomplete: "street-address", label: t(".address") %>
</div>

<div class="row">
<div class="columns medium-4">
<div class="field">
<%= f.text_field :postal_code, pattern: "^[0-9]+$", minlength: 5, maxlength: 5, autocomplete: "postal-code", label: t(".code") %>
</div>
</div>
<div class="columns medium-8">
<div class="field">
<%= f.text_field :city, label: t(".city") %>
</div>
</div>
</div>

<div class="field">
<%= f.check_box :certification, label: t(".certification") %>
</div>
</div>

<%= f.hidden_field :email %>
<%= f.hidden_field :uid %>
<%= f.hidden_field :name %>
<%= f.hidden_field :provider %>
<%= f.hidden_field :oauth_signature %>
<div class="actions">
<%= f.submit t(".complete_profile"), class: "button expanded" %>
</div>
<% end %>
</div>
</div>
</div>
</div>
<% end %>
</div>
</main>

<%= javascript_pack_tag "application" %>
<%= stylesheet_pack_tag "application" %>
1 change: 1 addition & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class Application < Rails::Application
require "extends/controllers/decidim/homepage_controller_extends"
require "extends/forms/decidim/admin/organization_appearance_form_extends"
require "extends/omniauth/strategies/france_connect_extends"
require "extends/forms/decidim/omniauth_registration_form_extend"
end

initializer "session cookie domain", after: "Expire sessions" do
Expand Down
16 changes: 16 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,19 @@ en:
osp_authorization_workflow:
name: Authorization procedure
devise:
omniauth_registrations:
new:
address: Address
birth_date: Date of birth
birth_date_help: You must be over 16 years old to access this service.
certification: CESE Certification
city: City
code: Postal code
complete_profile: Complete profile
personal_data_step: Complete your profile
registration_info: Please complete your profile
sign_up: Sign up
subtitle: Create your account
registrations:
form:
errors:
Expand All @@ -67,6 +80,9 @@ en:
sessions:
new:
sign_in_disabled: Sign in disabled
errors:
not_found:
back_home: Back to home
events:
budgets:
pending_order:
Expand Down
Loading
Loading