Merge pull request #592 from AyuntamientoMadrid/management-on-site-verification

Management on site verification
This commit is contained in:
Raimond Garcia
2015-10-09 17:35:19 +02:00
52 changed files with 986 additions and 59 deletions

View File

@@ -4,6 +4,7 @@
// 02. Sidebar
// 03. List elements
// 04. Stats
// 05. Management
//
// 01. Global styles
@@ -272,3 +273,37 @@ body.admin {
}
}
}
// 05. Management
// - - - - - - - - - - - - - - - - - - - - - - - - -
.postfix {
height: rem-calc(48);
line-height: rem-calc(48);
}
.user-permissions {
ul {
list-style-type: none;
margin-left: 0;
li {
font-size: rem-calc(14);
margin-bottom: rem-calc(12);
span {
color: $text-medium;
font-size: rem-calc(12);
}
.icon-check {
color: $check;
}
.icon-x {
color: $delete;
}
}
}
}

View File

@@ -0,0 +1,41 @@
class Management::DocumentVerificationsController < Management::BaseController
def index
@document_verification = Verification::Management::Document.new()
end
def check
@document_verification = Verification::Management::Document.new(document_verification_params)
if @document_verification.valid?
if @document_verification.verified?
render :verified
elsif @document_verification.user?
render :new
elsif @document_verification.in_census?
redirect_to new_management_email_verification_path(email_verification: document_verification_params)
else
render :invalid_document
end
else
render :index
end
end
def create
@document_verification = Verification::Management::Document.new(document_verification_params)
@document_verification.verify
render :verified
end
private
def document_verification_params
params.require(:document_verification).permit(:document_type, :document_number)
end
end

View File

@@ -0,0 +1,24 @@
class Management::EmailVerificationsController < Management::BaseController
def new
@email_verification = Verification::Management::Email.new(email_verification_params)
end
def create
@email_verification = Verification::Management::Email.new(email_verification_params)
if @email_verification.save
render :sent
else
render :new
end
end
private
def email_verification_params
params.require(:email_verification).permit(:document_type, :document_number, :email)
end
end

View File

@@ -0,0 +1,25 @@
class Management::UsersController < Management::BaseController
def new
@user = User.new(user_params)
end
def create
@user = User.new(user_params)
@user.skip_password_validation = true
@user.terms_of_service = '1'
@user.residence_verified_at = Time.now
@user.verified_at = Time.now
if @user.save then
render :show
else
render :new
end
end
private
def user_params
params.require(:user).permit(:document_type, :document_number, :username, :email)
end
end

View File

@@ -0,0 +1,51 @@
class Users::ConfirmationsController < Devise::ConfirmationsController
# new action, PATCH does not exist in the default Devise::ConfirmationsController
# PATCH /resource/confirmation
def update
self.resource = resource_class.find_by_confirmation_token(params[:confirmation_token])
if resource.encrypted_password.blank?
resource.assign_attributes(resource_params)
if resource.valid? # password is set correctly
resource.save
resource.confirm
set_flash_message(:notice, :confirmed) if is_flashing_format?
sign_in_and_redirect(resource_name, resource)
else
render :show
end
else
resource.errors.add(:email, :password_already_set)
respond_with_navigational(resource.errors, status: :unprocessable_entity){ render :new }
end
end
# GET /resource/confirmation?confirmation_token=abcdef
def show
# In the default implementation, this already confirms the resource:
# self.resource = self.resource = resource_class.confirm_by_token(params[:confirmation_token])
self.resource = resource_class.find_by_confirmation_token(params[:confirmation_token])
yield resource if block_given?
# New condition added to if: when no password was given, display the "show" view (which uses "update" above)
if resource.encrypted_password.blank?
respond_with_navigational(resource){ render :show }
elsif resource.errors.empty?
resource.confirm # Last change: confirm happens here for people with passwords instead of af the top of the show action
set_flash_message(:notice, :confirmed) if is_flashing_format?
respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, resource) }
else
respond_with_navigational(resource.errors, status: :unprocessable_entity){ render :new }
end
end
protected
def resource_params
params.require(resource_name).permit(:password, :password_confirmation, :email)
end
end

View File

@@ -1,11 +1,18 @@
class Users::SessionsController < Devise::SessionsController
def after_sign_in_path_for(resource)
if resource.show_welcome_screen?
if !verifying_via_email? && resource.show_welcome_screen?
welcome_path
else
root_path
super
end
end
private
def verifying_via_email?
stored_path = session[stored_location_key_for(resource)] || ""
stored_path[0..5] == "/email"
end
end

View File

@@ -6,7 +6,11 @@ class Verification::EmailController < ApplicationController
def show
if Verification::Email.find(current_user, params[:email_verification_token])
current_user.update(verified_at: Time.now)
current_user.update(verified_at: Time.now,
document_number: current_user.document_number || current_user.unconfirmed_document_number,
residence_verified_at: current_user.residence_verified_at || Time.now)
redirect_to account_path, notice: t('verification.email.show.flash.success')
else
redirect_to verified_user_path, alert: t('verification.email.show.alert.failure')
@@ -17,7 +21,11 @@ class Verification::EmailController < ApplicationController
@email = Verification::Email.new(@verified_user)
if @email.save
current_user.reload
Mailer.email_verification(current_user, @email.recipient, @email.encrypted_token).deliver_later
Mailer.email_verification(current_user,
@email.recipient,
@email.encrypted_token,
@verified_user.document_type,
@verified_user.document_number).deliver_later
redirect_to account_path, notice: t('verification.email.create.flash.success', email: @verified_user.email)
else
redirect_to verified_user_path, alert: t('verification.email.create.alert.failure')
@@ -33,4 +41,4 @@ class Verification::EmailController < ApplicationController
def verified_user_params
params.require(:verified_user).permit(:id)
end
end
end

View File

@@ -12,21 +12,10 @@ module AdminHelper
options
end
def humanize_document_type(document_type)
case document_type
when "1"
t "verification.residence.new.document_type.spanish_id"
when "2"
t "verification.residence.new.document_type.passport"
when "3"
t "verification.residence.new.document_type.residence_card"
end
end
private
def namespace
controller.class.parent.name.downcase
end
end
end

View File

@@ -0,0 +1,12 @@
module UserHelper
def humanize_document_type(document_type)
case document_type
when "1"
t "verification.residence.new.document_type.spanish_id"
when "2"
t "verification.residence.new.document_type.passport"
when "3"
t "verification.residence.new.document_type.residence_card"
end
end
end

View File

@@ -1,6 +1,7 @@
class Mailer < ApplicationMailer
helper :text_with_links
helper :mailer
helper :user
def comment(comment)
@comment = comment
@@ -16,10 +17,12 @@ class Mailer < ApplicationMailer
mail(to: @recipient.email, subject: t('mailers.reply.subject')) if @commentable.present? && @recipient.present?
end
def email_verification(user, recipient, token)
def email_verification(user, recipient, token, document_type, document_number)
@user = user
@recipient = recipient
@token = token
@document_type = document_type
@document_number = document_number
mail(to: @recipient, subject: t('mailers.email_verification.subject'))
end

View File

@@ -25,6 +25,7 @@ class User < ActiveRecord::Base
validates :username, presence: true, unless: :organization?
validates :username, uniqueness: true, unless: :organization?
validates :document_number, uniqueness: { scope: :document_type }, allow_nil: true
validate :validate_username_length
validates :official_level, inclusion: {in: 0..5}
@@ -35,11 +36,16 @@ class User < ActiveRecord::Base
accepts_nested_attributes_for :organization, update_only: true
attr_accessor :skip_password_validation
scope :administrators, -> { joins(:administrators) }
scope :moderators, -> { joins(:moderator) }
scope :organizations, -> { joins(:organization) }
scope :officials, -> { where("official_level > 0") }
scope :for_render, -> { includes(:organization) }
scope :by_document, -> (document_type, document_number) { where(document_type: document_type, document_number: document_number) }
before_validation :clean_document_number
def self.find_for_oauth(auth, signed_in_resource = nil)
# Get the identity and user if they exist
@@ -158,11 +164,20 @@ class User < ActiveRecord::Base
end
def show_welcome_screen?
sign_in_count == 1 && unverified? && !organization
sign_in_count == 1 && unverified? && !organization && !administrator?
end
def password_required?
return false if skip_password_validation
super
end
private
def clean_document_number
self.document_number = self.document_number.gsub(/[^a-z0-9]+/i, "").upcase unless self.document_number.blank?
end
def validate_username_length
validator = ActiveModel::Validations::LengthValidator.new(
attributes: :username,

View File

@@ -0,0 +1,34 @@
class Verification::Management::Document
include ActiveModel::Model
attr_accessor :document_type
attr_accessor :document_number
validates :document_type, :document_number, presence: true
delegate :username, :email, to: :user, allow_nil: true
def user
@user = User.by_document(document_type, document_number).first
end
def user?
user.present?
end
def in_census?
CensusApi.new.call(document_type, document_number).valid?
end
def verified?
user? && user.level_three_verified?
end
def verify
user.update(verified_at: Time.now) if user?
end
end

View File

@@ -0,0 +1,61 @@
class Verification::Management::Email
include ActiveModel::Model
attr_accessor :document_type
attr_accessor :document_number
attr_accessor :email
validates :document_type, :document_number, :email, presence: true
validates :email, format: { with: Devise.email_regexp }, allow_blank: true
validate :validate_user
validate :validate_document_number
delegate :username, to: :user, allow_nil: true
def user
@user ||= User.where(email: email).first
end
def user?
user.present?
end
def save
return false unless valid?
plain_token, encrypted_token = Devise.token_generator.generate(User, :email_verification_token)
user.update(document_type: document_type,
unconfirmed_document_number: document_number,
email_verification_token: plain_token)
Mailer.email_verification(user, email, encrypted_token, document_type, document_number).deliver_later
true
end
private
def validate_user
return if errors.count > 0
if !user?
errors.add(:email, I18n.t('errors.messages.user_not_found'))
elsif user.level_three_verified?
errors.add(:email, I18n.t('management.email_verifications.already_verified'))
end
end
def validate_document_number
return if errors.count > 0
if document_number_mismatch?
errors.add(:email,
I18n.t('management.email_verifications.document_mismatch',
document_type: ApplicationController.helpers.humanize_document_type(user.document_type),
document_number: user.document_number))
end
end
def document_number_mismatch?
user? && user.document_number.present? &&
(user.document_number != document_number || user.document_type != document_type)
end
end

View File

@@ -1,16 +1,23 @@
<% provide :title do %><%= t("devise_views.confirmations.title") %><% end %>
<h1 class="text-center"><%= t("devise_views.confirmations.title") %></h1>
<% provide :title do %><%= t("devise_views.confirmations.new.title") %><% end %>
<h1 class="text-center"><%= t("devise_views.confirmations.new.title") %></h1>
<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
<%= render 'shared/errors', resource: resource %>
<div class="row">
<div class="small-12 columns">
<%= f.email_field :email, autofocus: true, placeholder: t("devise_views.confirmations.email_label"), value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
<%= f.email_field :email, autofocus: true, placeholder: t("devise_views.confirmations.new.email_label"), value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
</div>
<% if @requires_password %>
<p><%= f.password_field :password %></p>
<p><%= f.password_field :password_confirmation %></p>
<% end %>
<%= hidden_field_tag :confirmation_token,@confirmation_token %>
<div class="small-12 columns">
<%= f.submit(t("devise_views.confirmations.submit"), class: "button radius expand") %>
<%= f.submit(t("devise_views.confirmations.new.submit"), class: "button radius expand") %>
</div>
</div>
<% end %>

View File

@@ -0,0 +1,34 @@
<% provide :title do %><%= t("devise_views.confirmations.show.title") %><% end %>
<h1 class="text-center"><%= t("devise_views.confirmations.show.title") %></h1>
<p><%= t('devise_views.confirmations.show.instructions_html', email: resource.email) %></p>
<%= form_for(resource,
as: resource_name,
url: update_user_confirmation_path,
html: { method: :patch }) do |f| %>
<p><%= t('devise_views.confirmations.show.please_set_password') %></p>
<%= render 'shared/errors', resource: resource %>
<div class="row">
<div class="small-12 columns">
<%= f.password_field :password,
autofocus: true,
label: t('devise_views.confirmations.show.new_password_label') %>
</div>
<div class="small-12 columns">
<%= f.password_field :password_confirmation,
label: t('devise_views.confirmations.show.new_password_confirmation_label') %>
</div>
</div>
<%= hidden_field_tag :confirmation_token, params[:confirmation_token] %>
<div class="small-12 columns">
<%= f.submit(t("devise_views.confirmations.show.submit"), class: "button radius expand") %>
</div>
<% end %>
<%= render "devise/shared/links" %>

View File

@@ -9,6 +9,12 @@
t('mailers.email_verification.click_here_to_verify'),
email_url(email_verification_token: @token))) %>
</p>
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 14px;font-weight: normal;line-height: 24px;">
<%= t("mailers.email_verification.instructions_2_html",
document_type: humanize_document_type(@document_type),
document_number: @document_number) %>
</p>
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 14px;font-weight: normal;line-height: 24px;">
<%= t("mailers.email_verification.thanks") %>
</p>

View File

@@ -0,0 +1,10 @@
<p class="account-info">
<%= t("management.document_type_label") %> <strong><%= humanize_document_type(account.document_type) %></strong>
<%= t("management.document_number") %> <strong><%= account.document_number %></strong>
<% if account.username.present? %>
<%= t("management.username_label") %> <strong><%= account.username %></strong>
<% end %>
<% if account.email.present? %>
<%= t("management.email_label") %> <strong><%= account.email %></strong>
<% end %>
</p>

View File

@@ -0,0 +1,16 @@
<nav class="admin-sidebar">
<ul id="admin_menu">
<li>
<%= link_to t("management.menu.title"), management_root_path %>
</li>
<li <%= "class=active" if controller_name == "document_verifications" ||
controller_name == "email_verifications" ||
controller_name == "users" %>>
<%= link_to management_document_verifications_path do %>
<i class="icon-tag"></i>
<%= t("management.menu.users") %>
<% end %>
</li>
</ul>
</nav>

View File

@@ -0,0 +1,20 @@
<%
# Parameters:
# message: A string explaining the permissions
# permissions: An array of symbols containing the permissions
# (can be :debates, :proposal, :support_proposal, :votes)
%>
<div class="user-permissions">
<p><%= message %></p>
<ul>
<% [:debates, :create_proposals, :support_proposals, :vote_proposals].each do |permission| %>
<li>
<i class="<%= permissions.include?(permission) ? 'icon-check' : 'icon-x' %>"></i>
<%= t("management.permissions.#{permission}") %>
</li>
<% end %>
</ul>
</div>

View File

@@ -0,0 +1,25 @@
<h2><%= t("management.document_verifications.title") %></h2>
<div class="row">
<div class="small-12 medium-8 column">
<%= form_for(@document_verification,
as: :document_verification,
url: check_management_document_verifications_path) do |f| %>
<div class="small-12 medium-4">
<%= f.select(:document_type,
[[humanize_document_type("1"), 1],
[humanize_document_type("2"), 2],
[humanize_document_type("3"), 3]],
label: t("management.document_type_label")) %>
</div>
<div class="small-12 medium-5">
<%= f.text_field :document_number,
placeholder: t('management.document_number'),
label: t("management.document_number")
%>
</div>
<%= f.submit t("management.check") %>
<% end %>
</div>
</div>

View File

@@ -0,0 +1,15 @@
<%= render 'management/account_info.html', account: @document_verification %>
<div class="alert-box alert radius">
<%= t("management.document_verifications.not_in_census") %>
</div>
<%= render 'management/user_permissions',
message: t("management.document_verifications.not_in_census_info"),
permissions: [:debates, :create_proposals] %>
<p>
<%= t("management.document_verifications.has_no_account_html",
link: link_to('http://decide.madrid.es', 'http://decide.madrid.es'),
target: "_blank") %>
</p>

View File

@@ -0,0 +1,17 @@
<%= render 'management/account_info.html', account: @document_verification %>
<div class="alert-box success radius">
<%= t("management.document_verifications.please_check_account_data") %>
</div>
<%= render 'management/user_permissions',
message: t("management.document_verifications.in_census_has_following_permissions"),
permissions: [:debates, :create_proposals, :support_proposals] %>
<%= form_for @document_verification,
as: :document_verification,
url: management_document_verifications_path do |f| %>
<%= f.hidden_field :document_type %>
<%= f.hidden_field :document_number %>
<%= f.submit t("management.document_verifications.verify"), class: "button success radius" %>
<% end %>

View File

@@ -0,0 +1,11 @@
<%= render 'management/account_info.html', account: @document_verification %>
<div class="alert-box success radius">
<%= t("management.document_verifications.already_verified") %>
</div>
<%= render 'management/user_permissions',
message: t("management.document_verifications.in_census_has_following_permissions"),
permissions: [:debates, :create_proposals, :support_proposals, :vote_proposals] %>
<a href="javascript:window.print();" class="button warning radius"><%= t("management.print_info") %></a>

View File

@@ -0,0 +1,31 @@
<%= render 'management/account_info.html', account: @email_verification %>
<div class="alert-box success radius">
<%= t("management.email_verifications.document_found_in_census") %>
</div>
<ul>
<li>
<strong><%= t("management.email_verifications.if_existing_account") %></strong>
<p><%= t("management.email_verifications.introduce_email") %></p>
<%= form_for @email_verification,
as: :email_verification,
url: management_email_verifications_path do |f| %>
<%= f.hidden_field :document_type %>
<%= f.hidden_field :document_number %>
<%= f.text_field :email, label: false, placeholder: t('management.email_verifications.email_placeholder') %>
<%= f.submit t("management.email_verifications.send_email"), class: "button success radius" %>
<% end %>
</li>
<li>
<strong><%= t("management.email_verifications.if_no_existing_account") %></strong>
<p class="margin-top">
<%= link_to t('management.users.create_user'), new_management_user_path(user: params[:email_verification]), class: "button warning radius" %>
</p>
</li>
</ul>

View File

@@ -0,0 +1,14 @@
<%= render 'management/account_info.html', account: @email_verification %>
<div class="alert-box success radius">
<%= t("management.email_verifications.email_sent_instructions") %>
</div>
<%= render 'management/user_permissions',
message: t("management.email_verifications.document_found_in_census"),
permissions: [:debates, :create_proposals, :support_proposals, :vote_proposals] %>
<p>
<a href="javascript:window.print();" class="button warning radius"><%= t("management.print_info") %></a>
</p>

View File

@@ -0,0 +1,25 @@
<%= render 'management/account_info.html', account: @user %>
<p><%= t("management.users.create_user_info") %></p>
<%= render 'management/user_permissions',
message: t("management.document_verifications.in_census_has_following_permissions"),
permissions: [:debates, :create_proposals, :support_proposals, :vote_proposals] %>
<div class="row">
<div class="small-12 medium-9 column">
<%= form_for @user, url: management_users_path do |f| %>
<%= f.hidden_field :document_type %>
<%= f.hidden_field :document_number %>
<%= f.text_field :username,
label: t('management.username_label'),
placeholder: t('management.username_label') %>
<%= f.text_field :email,
label: t('management.email_label'),
placeholder: t('management.email_label') %>
<%= f.submit t("management.users.create_user_submit"), class: "button success radius" %>
<% end %>
</div>
</div>

View File

@@ -0,0 +1,7 @@
<%= render 'management/account_info.html', account: @user %>
<p><%= t("management.users.create_user_success_html", email: @user.email) %></p>
<%= render 'management/user_permissions',
message: t("management.document_verifications.in_census_has_following_permissions"),
permissions: [:debates, :create_proposals, :support_proposals, :vote_proposals] %>

View File

@@ -1,5 +1,11 @@
en:
activerecord:
errors:
models:
user:
attributes:
email:
password_already_set: "This user already has a password"
models:
activity: Activity
comment: Comment

View File

@@ -1,5 +1,11 @@
es:
activerecord:
errors:
models:
user:
attributes:
email:
password_already_set: "Este usuario ya tiene una clave asociada"
models:
activity:
one: actividad

View File

@@ -1,9 +1,17 @@
en:
devise_views:
confirmations:
title: "Resend confirmation instructions"
email_label: Email
submit: "Resend confirmation"
new:
title: "Resend confirmation instructions"
email_label: Email
submit: "Resend confirmation"
show:
title: "Confirm my account"
instructions_html: "Confirming the account with email %{email}"
please_set_password: "Please choose your new pasword (it will allow you to login with the email above)"
new_password_label: "New access password"
new_password_confirmation_label: "Repeat access password"
submit: "Confirm"
mailer:
confirmation_instructions:
title: "Welcome to open government portal"

View File

@@ -1,9 +1,17 @@
es:
devise_views:
confirmations:
title: "Reenviar instrucciones de confirmación"
email_label: Email
submit: "Reenviar instrucciones"
new:
title: "Reenviar instrucciones de confirmación"
email_label: Email
submit: "Reenviar instrucciones"
show:
title: "Confirmar mi cuenta"
instructions_html: "Vamos a proceder a confirmar la cuenta con el email <b>%{email}</b>"
please_set_password: "Por favor introduce una nueva clave de acceso para su cuenta (te permitirá hacer login con el email de más arriba)"
new_password_label: "Nueva clave de acceso"
new_password_confirmation_label: "Repite la clave de nuevo"
submit: "Confirmar"
mailer:
confirmation_instructions:
title: "Te damos la bienvenida al Portal de Gobierno Abierto del Ayuntamiento de Madrid"

View File

@@ -1,5 +1,8 @@
en:
locale: English
errors:
messages:
user_not_found: "User not found"
layouts:
header:
external_link_transparency: Transparency

View File

@@ -1,5 +1,8 @@
es:
locale: "Español"
errors:
messages:
user_not_found: "No se encontró el usuario"
layouts:
header:
external_link_transparency: Transparencia

View File

@@ -15,5 +15,6 @@ en:
title: Please verify yourself
instructions_html: "We need to verify you using this email, which we got from the Census. %{verification_link}"
click_here_to_verify: "Please click here to verify yourself"
instructions_2_html: "This email will verify your account with <b>%{document_type} %{document_number}</b>. If these don't belong to you, please don't click on the previous link and ignore this email."
thanks: "Thanks"

View File

@@ -15,4 +15,5 @@ es:
title: Verifica tu cuenta con el siguiente enlace
instructions_html: "Para terminar de verificar tu cuenta de usuario en el Portal de Gobierno Abierto del Ayuntamiento de Madrid, necesitamos que pulses %{verification_link}."
click_here_to_verify: "en este enlace"
instructions_2_html: "Este email es para verificar tu cuenta con <b>%{document_type} %{document_number}</b>. Si esos no son tus datos, por favor no pulses el enlace anterior e ignora este email."
thanks: "Muchas gracias."

View File

@@ -1,5 +1,46 @@
en:
management:
print_info: "Print this info"
username_label: "User name"
email_label: "Email"
check: "Check"
document_number: "Document number"
document_type_label: "Document type"
menu:
title: "Management"
users: "Users"
dashboard:
index:
title: Management Dashboard
title: "Management"
permissions:
debates: "Engage in debates"
create_proposals: "Create proposals"
support_proposals: "Support proposals"
vote_proposals: "Vote proposals"
document_verifications:
title: "User management"
please_check_account_data: "Please check that the account data above are correct."
already_verified: "This user account is already verified."
in_census_has_following_permissions: "This user can participate in the website with the following permissions:"
not_in_census: "This document is not registered in Madrid."
not_in_census_info: "Citizens not in the Census can participate in the website with the following permissions:"
has_no_account_html: "In order to create an account, go to %{link} and click in <b>'Register'</b> in the upper-left part of the screen."
verify: "Verify"
email_verifications:
document_found_in_census: "This document was found in the census, but it has no user account associated to it. Please choose one of the following options:"
if_existing_account: "If the person has already a user account created in the website,"
introduce_email: "Please introduce the email used on the account:"
email_placeholder: "Write the email this person used to create his or her account"
send_email: "Send verification email"
email_sent_instructions: "In order to completely verify this user, it is necessary that the user clicks on a link which we have sent to the email address above. This step is needed in order to confirm that the address belongs to him."
if_no_existing_account: "If this person has not created an account yet"
document_mismatch: "This email belongs to a user which already has an associated id: %{document_number}(%{document_type})"
already_verified: "This user account is already verified."
users:
create_user: "Create a new account"
create_user_info: "We will create an account with the following data:"
create_user_submit: "Create user"
create_user_success_html:
"We have sent an email to the email address <b>%{email}</b> in order to verify that it belongs to this user.
It contains a link they have to click. Then they will have to set their access password before being able
to log in to the website"

View File

@@ -1,5 +1,46 @@
es:
management:
print_info: "Imprimir esta información"
username_label: "Nombre de usuario"
email_label: "Email"
check: "Comprobar"
document_number: "Número de documento"
document_type_label: "Tipo de documento"
menu:
title: "Gestión"
users: "Usuarios"
dashboard:
index:
title: Gestión
title: "Gestión"
permissions:
debates: "Participar en debates"
create_proposals: "Crear nuevas propuestas"
support_proposals: "Apoyar propuestas"
vote_proposals: "Participar en las votaciones finales"
document_verifications:
title: "Gestión de usuarios"
please_check_account_data: "Compruebe que los datos anteriores son correctos para proceder a verificar la cuenta completamente."
already_verified: "Esta cuenta de usuario ya está verificada."
in_census_has_following_permissions: "Este usuario puede participar en el Portal de Gobierno Abierto del Ayuntamiento de Madrid con las siguientes posibilidades:"
not_in_census: "Este documento no está registrado en el Padrón Municipal de Madrid."
not_in_census_info: "Las personas no empadronadas en Madrid pueden participar en el Portal de Gobierno Abierto del Ayuntamiento de Madrid con las siguientes posibilidades:"
has_no_account_html: "Para crear un usuario entre en %{link} y haga clic en la opción <b>'Registrarse'</b> en la parte superior derecha de la pantalla."
verify: "Verificar usuario"
email_verifications:
document_found_in_census: "Este documento está en el registro del padrón municipal, pero todavía no tiene una cuenta de usuario asociada. Elige una de las opciones siguientes:"
if_existing_account: "Si la persona ya ha creado una cuenta de usuario en la web"
introduce_email: "Introduce el email con el que creó la cuenta:"
email_placeholder: "Introduce el email de registro"
send_email: "Enviar email de verificación"
if_no_existing_account: "Si la persona todavía no ha creado una cuenta de usuario en la web"
email_sent_instructions: "Para terminar de verificar esta cuenta es necesario que haga clic en el enlace que le hemos enviado a la dirección de correo que figura arriba. Este paso es necesario para confirmar que dicha cuenta de usuario es suya."
document_mismatch: "Ese email corresponde a un usuario que ya tiene asociado el documento %{document_number}(%{document_type})"
already_verified: "Esta cuenta de usuario ya está verificada."
users:
create_user: "Crear nueva cuenta de usuario"
create_user_info: "Procedemos a crear un usuario con la siguiente información:"
create_user_submit: "Crear usuario"
create_user_success_html:
"Hemos enviado un correo electrónico a <b>%{email}</b> para verificar que es suya.
El correo enviado contiene un link que el usuario deberá pulsar. Entonces podrá seleccionar
una clave de acceso, y entrar en la web de participación."

View File

@@ -1,7 +1,13 @@
Rails.application.routes.draw do
as :user do
match '/user/confirmation' => 'users/confirmations#update', :via => :patch, :as => :update_user_confirmation
end
devise_for :users, controllers: {
registrations: 'users/registrations',
sessions: 'users/sessions',
confirmations: 'users/confirmations',
omniauth_callbacks: 'users/omniauth_callbacks'
}
devise_for :organizations, class_name: 'User',
@@ -166,6 +172,16 @@ Rails.application.routes.draw do
namespace :management do
root to: "dashboard#index"
resources :document_verifications, only: [:index, :new, :create] do
collection do
post :check
end
end
resources :email_verifications, only: [:new, :create]
resources :users, only: [:new, :create]
get 'sign_in', to: 'sessions#create'
resources :sessions, only: :create

View File

@@ -50,9 +50,10 @@ end
(1..40).each do |i|
user = create_user("user#{i}@madrid.es")
level = [1,2,3].sample
if level == 2 then
user.update(residence_verified_at: Time.now, confirmed_phone: Faker::PhoneNumber.phone_number, document_number: Faker::Number.number(10) )
elsif level == 3 then
if level >= 2 then
user.update(residence_verified_at: Time.now, confirmed_phone: Faker::PhoneNumber.phone_number, document_number: Faker::Number.number(10), document_type: "1" )
end
if level == 3 then
user.update(verified_at: Time.now, document_number: Faker::Number.number(10) )
end
end

View File

@@ -0,0 +1,5 @@
class AddUnconfirmedDocumentNumberToUser < ActiveRecord::Migration
def change
add_column :users, :unconfirmed_document_number, :string
end
end

View File

@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150930082311) do
ActiveRecord::Schema.define(version: 20151002144206) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -267,30 +267,30 @@ ActiveRecord::Schema.define(version: 20150930082311) do
add_index "tags", ["proposals_count"], name: "index_tags_on_proposals_count", using: :btree
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.boolean "email_on_comment", default: false
t.boolean "email_on_comment_reply", default: false
t.string "phone_number", limit: 30
t.boolean "email_on_comment", default: false
t.boolean "email_on_comment_reply", default: false
t.string "phone_number", limit: 30
t.string "official_position"
t.integer "official_level", default: 0
t.integer "official_level", default: 0
t.datetime "hidden_at"
t.string "sms_confirmation_code"
t.string "username", limit: 60
t.string "username", limit: 60
t.string "document_number"
t.string "document_type"
t.datetime "residence_verified_at"
@@ -302,7 +302,8 @@ ActiveRecord::Schema.define(version: 20150930082311) do
t.datetime "letter_requested_at"
t.datetime "confirmed_hide_at"
t.string "letter_verification_code"
t.integer "failed_census_calls_count", default: 0
t.integer "failed_census_calls_count", default: 0
t.string "unconfirmed_document_number"
end
add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree

View File

@@ -1,8 +1,11 @@
FactoryGirl.define do
sequence(:document_number) { |n| "#{n.to_s.rjust(8, '0')}X" }
factory :user do
sequence(:username) { |n| "Manuela#{n}" }
sequence(:email) { |n| "manuela#{n}@madrid.es" }
password 'judgmentday'
terms_of_service '1'
confirmed_at { Time.now }
@@ -18,12 +21,14 @@ FactoryGirl.define do
unconfirmed_phone "611111111"
confirmed_phone "611111111"
sms_confirmation_code "1234"
document_number "12345678Z"
document_type "1"
document_number
end
trait :level_three do
verified_at Time.now
document_number "12345678Z"
document_type "1"
document_number
end
trait :hidden do
@@ -49,8 +54,8 @@ FactoryGirl.define do
factory :verification_residence, class: Verification::Residence do
user
document_number '12345678Z'
document_type 1
document_number
document_type "1"
date_of_birth Date.new(1980, 12, 31)
postal_code "28013"
terms_of_service '1'
@@ -62,7 +67,7 @@ FactoryGirl.define do
factory :failed_census_call do
user
document_number '11111111A'
document_number
document_type 1
date_of_birth Date.new(1900, 1, 1)
postal_code '28000'
@@ -83,7 +88,7 @@ FactoryGirl.define do
end
factory :verified_user do
document_number '12345678Z'
document_number
document_type 'dni'
end

View File

@@ -44,7 +44,7 @@ feature 'Incomplete verifications' do
within "#user_#{incompletely_verified_user.id}" do
expect(page).to have_content "Spanish ID"
expect(page).to have_content "11111111A"
expect(page).to have_content incompletely_verified_user.document_number
expect(page).to have_content Date.new(1900, 1, 1)
expect(page).to have_content "28000"
end

View File

@@ -0,0 +1,56 @@
require 'rails_helper'
feature 'DocumentVerifications' do
background do
login_as_manager(create(:manager))
end
scenario 'Verifying a level 3 user shows an "already verified" page' do
user = create(:user, :level_three)
visit management_document_verifications_path
fill_in 'document_verification_document_number', with: user.document_number
click_button 'Check'
expect(page).to have_content "already verified"
end
scenario 'Verifying a level 2 user displays the verification form' do
user = create(:user, :level_two)
visit management_document_verifications_path
fill_in 'document_verification_document_number', with: user.document_number
click_button 'Check'
expect(page).to have_content "Vote proposals"
click_button 'Verify'
expect(page).to have_content "already verified"
expect(user.reload).to be_level_three_verified
end
scenario 'Verifying a user which does not exist and is not in the census shows an error' do
expect_any_instance_of(Verification::Management::Document).to receive(:in_census?).and_return(false)
visit management_document_verifications_path
fill_in 'document_verification_document_number', with: "inexisting"
click_button 'Check'
expect(page).to have_content "This document is not registered"
end
scenario 'Verifying a user which does exists in the census but not in the db redirects allows sending an email' do
visit management_document_verifications_path
fill_in 'document_verification_document_number', with: '1234'
click_button 'Check'
expect(page).to have_content "Please introduce the email used on the account"
end
end

View File

@@ -0,0 +1,41 @@
require 'rails_helper'
feature 'EmailVerifications' do
scenario 'Verifying a level 1 user via email' do
login_as_manager(create(:manager))
user = create(:user)
visit management_document_verifications_path
fill_in 'document_verification_document_number', with: '1234'
click_button 'Check'
expect(page).to have_content "Please introduce the email used on the account"
fill_in 'email_verification_email', with: user.email
click_button 'Send verification email'
expect(page).to have_content("In order to completely verify this user, it is necessary that the user clicks on a link")
user.reload
login_as(user)
sent_token = /.*email_verification_token=(.*)".*/.match(ActionMailer::Base.deliveries.last.body.to_s)[1]
visit email_path(email_verification_token: sent_token)
expect(page).to have_content "You are now a verified user"
expect(page).to_not have_link "Verify my account"
expect(page).to have_content "Verified account"
expect(user.reload.document_number).to eq('1234')
expect(user).to be_level_three_verified
end
end

View File

@@ -0,0 +1,47 @@
require 'rails_helper'
feature 'users' do
scenario 'Creating a level 3 user from scratch' do
login_as_manager(create(:manager))
visit management_document_verifications_path
fill_in 'document_verification_document_number', with: '1234'
click_button 'Check'
expect(page).to have_content "Please introduce the email used on the account"
click_link 'Create a new account'
fill_in 'user_username', with: 'pepe'
fill_in 'user_email', with: 'pepe@gmail.com'
click_button 'Create user'
expect(page).to have_content "We have sent an email"
user = User.find_by_email('pepe@gmail.com')
expect(user).to be_level_three_verified
expect(user).to be_residence_verified
expect(user).to_not be_confirmed
sent_token = /.*confirmation_token=(.*)".*/.match(ActionMailer::Base.deliveries.last.body.to_s)[1]
visit user_confirmation_path(confirmation_token: sent_token)
expect(page).to have_content "Confirming the account with email"
fill_in 'user_password', with: '12345678'
fill_in 'user_password_confirmation', with: '12345678'
click_button 'Confirm'
expect(user.reload).to be_confirmed
expect(page).to have_content "Your email address has been successfully confirmed."
end
end

View File

@@ -10,6 +10,24 @@ feature "Welcome screen" do
expect(current_path).to eq(welcome_path)
end
scenario 'a regular user does not see it when coing to /email' do
plain, encrypted = Devise.token_generator.generate(User, :email_verification_token)
user = create(:user, email_verification_token: plain)
visit email_path(email_verification_token: encrypted)
fill_in 'user_email', with: user.email
fill_in 'user_password', with: user.password
click_button 'Log in'
expect(page).to have_content("You are now a verified user")
expect(current_path).to eq(account_path)
end
scenario 'it is not shown more than once' do
user = create(:user, sign_in_count: 2)
@@ -42,4 +60,12 @@ feature "Welcome screen" do
expect(current_path).to eq(proposals_path)
end
scenario 'is not shown to administrators' do
administrator = create(:administrator)
login_through_form_as(administrator.user)
expect(current_path).to eq(proposals_path)
end
end

View File

@@ -1,6 +1,6 @@
require 'rails_helper'
describe AdminHelper do
describe UserHelper do
describe '#humanize_document_type' do
it "should return a humanized document type" do
@@ -10,4 +10,4 @@ describe AdminHelper do
end
end
end
end

View File

@@ -2,7 +2,7 @@ require 'rails_helper'
describe Verification::Residence do
let(:residence) { build(:verification_residence) }
let(:residence) { build(:verification_residence, document_number: "12345678Z") }
describe "validations" do
@@ -57,7 +57,7 @@ describe Verification::Residence do
residence.user = user
residence.save
residence2 = build(:verification_residence)
build(:verification_residence)
residence.valid?
expect(residence.errors[:document_number]).to include("has already been taken")
@@ -112,7 +112,7 @@ describe Verification::Residence do
describe "Failed census call" do
it "stores failed census API calls" do
residence = build(:verification_residence, :invalid)
residence = build(:verification_residence, :invalid, document_number: "12345678Z")
residence.save
expect(FailedCensusCall.count).to eq(1)
@@ -126,4 +126,4 @@ describe Verification::Residence do
end
end
end
end

View File

@@ -287,4 +287,19 @@ describe User do
end
describe "document_number" do
it "should upcase document number" do
user = User.new({document_number: "x1234567z"})
user.valid?
expect(user.document_number).to eq("X1234567Z")
end
it "should remove all characters except numbers and letters" do
user = User.new({document_number: " 12.345.678 - B"})
user.valid?
expect(user.document_number).to eq("12345678B")
end
end
end

View File

@@ -0,0 +1,49 @@
require 'rails_helper'
describe Verification::Management::Email do
describe "#user" do
subject { described_class.new(document_type: "1", document_number: "1234", email: "inexisting@gmail.com") }
it "returns nil/false when the user does not exist" do
expect(subject.user).to be_nil
expect(subject.user?).to_not be
end
end
describe "validations" do
it "is not valid if the user does not exist" do
expect(described_class.new(document_type: "1", document_number: "1234", email: "inexisting@gmail.com")).to_not be_valid
end
it "is not valid if the user is already level 3" do
user = create(:user, :level_three)
expect(described_class.new(document_type: "1", document_number: "1234", email: user.email)).to_not be_valid
end
it "is not valid if the user already has a different document number" do
user = create(:user, document_number: "1234", document_type: "1")
expect(described_class.new(document_type: "1", document_number: "5678", email: user.email)).to_not be_valid
end
end
describe "#save" do
it "does nothing if not valid" do
expect(described_class.new(document_type: "1", document_number: "1234", email: "inexisting@gmail.com").save).to eq(false)
end
it "updates the user and sends an email" do
user = create(:user)
validation = described_class.new(document_type: "1", document_number: "1234", email: user.email)
mail = double(:mail)
allow(validation).to receive(:user).and_return user
expect(mail).to receive(:deliver_later)
expect(Devise.token_generator).to receive(:generate).with(User, :email_verification_token).and_return(["1","2"])
expect(user).to receive(:update).with(document_type: "1", unconfirmed_document_number: "1234", email_verification_token: "1")
expect(Mailer).to receive(:email_verification).with(user, user.email, "2", "1", "1234").and_return(mail)
validation.save
end
end
end

View File

@@ -14,6 +14,10 @@ module CommonActions
click_button 'Sign up'
end
def login_as_manager(manager)
visit management_sign_in_path(login: manager.username, clave_usuario: manager.password)
end
def login_through_form_as(user)
visit root_path
click_link 'Log in'