Skip to content

Commit

Permalink
add user edit ability, allow to specify username
Browse files Browse the repository at this point in the history
  • Loading branch information
ubergesundheit committed Jul 23, 2014
1 parent 5cb866c commit 11b14ec
Show file tree
Hide file tree
Showing 14 changed files with 200 additions and 20 deletions.
1 change: 1 addition & 0 deletions app/admin/contribution.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
column :delete_reason
column :start_date
column :end_date
column :user
column :category do |color|
raw("<span class=\"status_tag\" style=\"color: black;border:1px solid black;background:#{color.category_color};\">#{color.category}</span>")
end
Expand Down
33 changes: 32 additions & 1 deletion app/assets/javascripts/angular/services/User.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ angular.module('DialogMapApp')
'ngDialog'
'$rootScope'
'$timeout'
(Auth, ngDialog, $rootScope, $timeout) ->
'$http'
(Auth, ngDialog, $rootScope, $timeout, $http) ->
_user = {}

_user.isAuthenticated = Auth.isAuthenticated
Expand All @@ -47,8 +48,10 @@ angular.module('DialogMapApp')
scope.loading = false
if user.confirmed is true
scope.user =
name: user.name
email: user.email
id: user.id
external_auth: user.external_auth
_user.user = scope.user
else
scope.needConfirmation = true
Expand All @@ -61,6 +64,11 @@ angular.module('DialogMapApp')
scope.loading = false
return

handleUserEdit = ->
scope.stopUserEdit()
scope.performLogout()
return

# everything inside this scope are actions and variables for the modal
angular.extend scope,
loginCredentials: {}
Expand All @@ -69,6 +77,7 @@ angular.module('DialogMapApp')
scope.loading = true
Auth.register
email: @registerCredentials.email
name: @registerCredentials.name
password: @registerCredentials.password
password_confirmation: @registerCredentials.password_confirmation
.then handleUser, handleErrors
Expand Down Expand Up @@ -105,6 +114,27 @@ angular.module('DialogMapApp')
$timeout(checkLoginWindowClosed,500)
return)()
return
performUpdate: ->
scope.loading = true
$http.put '/api/users',
user:
email: scope.editCredentials.email
name: scope.editCredentials.name
current_password: scope.editCredentials.current_password
password: scope.editCredentials.password
password_confirmation: scope.editCredentials.password_confirmation
.then handleUserEdit, handleErrors
return
startUserEdit: ->
scope.editCredentials =
email: scope.user.email
name: scope.user.name
scope.showUserEdit = true
return
stopUserEdit: ->
scope.editCredentials = {}
scope.showUserEdit = false
return

# show the User Modal (Login/Register/User signout)
_user.showUserModal = ->
Expand All @@ -126,6 +156,7 @@ angular.module('DialogMapApp')
scope.loginCredentials = {}
scope.registerCredentials = {}
scope.needConfirmation = false
scope.stopUserEdit()
return

scope.$on 'ngDialog.opened', (e, $dialog) ->
Expand Down
90 changes: 86 additions & 4 deletions app/assets/javascripts/templates/user_modal.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@
.field_error{ ng_show: "register_form.email.$dirty && register_form.email.$invalid || errors.email" }
%span{ ng_show: "errors.email", ng_repeat: "error in errors.email"} {{ error }}
%span{ ng_show: "register_form.email.$error.email" } muss eine E-Mail Adresse sein
.field_label_container
.field_label Benutzername
%input#email.username{ name: "name",
ng_class: "{error: register_form.name.$dirty && register_form.name.$invalid}",
ng_model: "registerCredentials.name",
type: "text",
required: true,
ng_minlength: 4 }
.field_error_container
.field_error{ ng_show: "register_form.name.$dirty && register_form.name.$invalid || errors.name" }
%span{ ng_show: "errors.name", ng_repeat: "error in errors.name"} {{ error }}
%span{ ng_show: "register_form.name.$error.minlength" } muss mindestens 4 Zeichen enthalten
.field_label_container
.field_label Passwort
%input#password.password{ name: "password",
Expand Down Expand Up @@ -46,7 +58,7 @@
.submit-button-container
%i.fa.fa-check.pointer.submit-button{ ng_click: "!register_form.$invalid && performRegister()", ng_disabled: "register_form.$invalid" } Registrieren
%div{ ng_show: "needConfirmation" }
Danke für deine Registrierung.<br />Bitte schau in dein Postfach für die Adresse {{user.email}} und folge den Anweisungen, um deinen Account freizuschalten.
Danke für deine Registrierung.<br />Bitte schau in dein Postfach für die von dir angegebene Adresse und folge den Anweisungen, um deinen Account freizuschalten.
.submit-button-container
%i.fa.fa-check.pointer.submit-button{ ng_click: "needConfirmation = false; showLoginTab = false" } zurück zum Login
Expand All @@ -68,6 +80,76 @@
%i.fa.fa-check.pointer.submit-button{ ng_click: "performLogin()" } Anmelden
#modal_user{ ng_show: "authenticated" }
%h3 Eingeloggt als
%p.user-email {{ user.email }}
%i.fa.fa-power-off.submit-button.pointer.logout-link{ ng_click: "performLogout()" } Ausloggen
%div{ ng_hide: "showUserEdit" }
%h3 Eingeloggt als
%p.user-name
{{ user.name }}
%i.fa.fa-pencil.pointer{ ng_click: "startUserEdit()" }
%p.user-email
{{ user.email }}
%i.fa.fa-pencil.pointer{ ng_click: "startUserEdit()" }
%i.fa.fa-power-off.submit-button.pointer.logout-link{ ng_click: "performLogout()" } Ausloggen
%div{ ng_show: "showUserEdit" }
%h3 Benutzer bearbeiten
%i.fa.fa-times.pointer.submit-button.logout-link{ ng_click: "stopUserEdit()" } Abbrechen
%form{ id: "edit_form", name: "edit_form", novalidate: true }
.field_label_container
.field_label E-Mail
%input#email.username{ name: "email",
ng_class: "{error: edit_form.email.$dirty && edit_form.email.$invalid}",
ng_model: "editCredentials.email",
type: "email",
required: true }
.field_error_container
.field_error{ ng_show: "edit_form.email.$dirty && edit_form.email.$invalid || errors.email" }
%span{ ng_show: "errors.email", ng_repeat: "error in errors.email"} {{ error }}
%span{ ng_show: "edit_form.email.$error.email" } muss eine E-Mail Adresse sein
.field_label_container
.field_label Benutzername
%input#email.username{ name: "name",
ng_class: "{error: edit_form.name.$dirty && edit_form.name.$invalid}",
ng_model: "editCredentials.name",
type: "text",
required: true,
ng_minlength: 4 }
.field_error_container
.field_error{ ng_show: "edit_form.name.$dirty && edit_form.name.$invalid || errors.name" }
%span{ ng_show: "errors.name", ng_repeat: "error in errors.name"} {{ error }}
%span{ ng_show: "edit_form.name.$error.minlength" } muss mindestens 4 Zeichen enthalten
%div{ ng_if: "!user.external_auth" }
.field_label_container
.field_label Neues Passwort
%input#edit-password.password{ name: "password",
ng_class: "{error: edit_form.password.$dirty && edit_form.password.$invalid}",
ng_model: "editCredentials.password",
type: "password",
title: "muss mindestens 6 Zeichen enthalten",
ng_minlength: 6 }
.field_error_container
.field_error{ ng_show: "edit_form.password.$dirty && edit_form.password.$invalid || errors.password" }
%span{ ng_show: "errors.password", ng_repeat: "error in errors.password"} {{ error }}
%span{ ng_show: "edit_form.password.$error.minlength" } muss mindestens 6 Zeichen enthalten
.field_label_container
.field_label Passwortwiederholung
%input#password_confirmation.password{ name: "password_confirmation",
ng_class: "{error: edit_form.password_confirmation.$dirty && edit_form.password_confirmation.$invalid}",
ng_model: "editCredentials.password_confirmation",
type: "password",
password_match: "edit-password",
ng_minlength: 6}
.field_error_container
.field_error{ ng_show: "edit_form.password_confirmation.$dirty && edit_form.password_confirmation.$invalid || edit_form.password_confirmation.$error.pwmatch || errors.password_confirmation" }
%span{ ng_show: "edit_form.password_confirmation.$error.pwmatch" } stimmt nicht mit der Bestätigung überein
%span{ ng_show: "errors.password_confirmation", ng_repeat: "error in errors.password_confirmation"} {{ error }}
%hr
.field_label_container
.field_label Aktuelles Passwort
%input#password.password{ name: "current_password",
ng_class: "{error: edit_form.current_password.$dirty && edit_form.current_password.$invalid}",
ng_model: "editCredentials.current_password",
type: "password"}
.field_error_container
.field_error{ ng_show: "edit_form.current_password.$dirty && edit_form.current_password.$invalid || errors.current_password" }
%span{ ng_show: "errors.current_password", ng_repeat: "error in errors.current_password"} {{ error }}
.submit-button-container
%i.fa.fa-check.pointer.submit-button{ ng_click: "!edit_form.$invalid && performUpdate()", ng_disabled: "edit_form.$invalid" } Änderungen speichern
23 changes: 18 additions & 5 deletions app/assets/stylesheets/user.css.sass
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,32 @@
font-size: 11px
font-style: italic

.user-email
font-style: italic
margin: 10px 0px

.logout-link
display: block
text-align: center
&:hover
color: firebrick
&:before
margin: 0px 4px

.user-email, .logout-link
.user-email, .user-name
text-align: center
font-style: italic
margin: 10px 0px
&:before
font-style: normal
font-weight: 700
font-variant: small-caps

.user-email:before
content: "E-Mail: "

.user-name:before
content: "Benutzername: "

.edit-button
text-align: center
display: block

.social-login-button
cursor: pointer
Expand Down
8 changes: 8 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,13 @@ class ApplicationController < ActionController::Base
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
respond_to :html, :json
before_action :configure_permitted_parameters, if: :devise_controller?

protected

def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :name
devise_parameter_sanitizer.for(:account_update) << :name
end

end
28 changes: 28 additions & 0 deletions app/controllers/registrations_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class RegistrationsController < Devise::RegistrationsController
def update
@user = User.find(current_user.id)

successfully_updated = if needs_password?(@user)
@user.update_with_password(devise_parameter_sanitizer.sanitize(:account_update))
else
# remove the virtual current_password attribute
# update_without_password doesn't know how to ignore it
params[:user].delete(:current_password)
@user.update_without_password(devise_parameter_sanitizer.sanitize(:account_update))
end

if successfully_updated
head :no_content
else
render json: { errors: @user.errors }, status: :unprocessable_entity
end
end

private

# check if we need password to update user data
# ie if user signed in via an external provider
def needs_password?(user)
Identity.where(user_id: user.id).count == 0
end
end
3 changes: 2 additions & 1 deletion app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ class UsersController < ApplicationController
before_action :set_user, :finish_signup

def finish_signup
if request.patch? && params[:user] #&& params[:user][:email]
if request.patch? && params[:user] && params[:user][:email] && params[:user][:name]
current_user.skip_reconfirmation!
if current_user.update(user_params)
sign_in(current_user, :bypass => true)
redirect_to '/finish_signin.html'
else
@show_errors = true
render :layout => 'application_without_angular'
end
elsif request.get? && current_user.email.match(User::TEMP_EMAIL_REGEX).nil?
redirect_to '/finish_signin.html'
Expand Down
2 changes: 2 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ class User < ActiveRecord::Base
has_many :contributions

validates_format_of :email, :without => TEMP_EMAIL_REGEX, on: :update
validates :name, presence: true
validates_uniqueness_of :name, :case_sensitive => false

def self.find_for_oauth(auth, signed_in_resource = nil)

Expand Down
6 changes: 5 additions & 1 deletion app/serializers/user_serializer.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
class UserSerializer < ActiveModel::Serializer
attributes :id, :email, :name, :confirmed
attributes :id, :email, :name, :confirmed, :external_auth

def confirmed
object.confirmed?
end

def external_auth
Identity.where(user_id: object.id).count != 0
end
end
2 changes: 1 addition & 1 deletion app/views/layouts/application_without_angular.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
%meta{:content => "IE=edge,chrome=1", "http-equiv" => "X-UA-Compatible"}/
%meta{:content => "width=device-width, initial-scale=1.0, maximum-scale=1.0", :name => "viewport"}
%link{rel: "icon", href: "favicon.png", type: "image/png"}
<style>#add-email{width: 400px;margin:auto}input{text-align: center;margin: auto;display: block;}#user_email{width: 100%;}p.help-block{text-align: justify;}p.no-spam{text-align:center;}</style>
<style>#add-email{width: 400px;margin:auto}input{text-align: center;margin: auto;display: block;}input{width: 100%;}p.help-block{text-align: justify;}p.no-spam{text-align:center;}</style>
= csrf_meta_tags
%body
Expand Down
14 changes: 9 additions & 5 deletions app/views/users/finish_signup.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
#error_explanation
Fehler:
- current_user.errors.full_messages.each do |msg|
= msg
%p
= msg
%p.help-block Leider wird bei der Authentifizierung mit Twitter keine E-Mail Adresse mitgeschickt. Bitte vervollständige sie an dieser Stelle, damit andere Benutzer dich eindeutig identifizieren können.
= current_user.name
#form-group
.form-group
= f.label :name, "Benutzername (mindestens 4 Zeichen)"
.controls
= f.text_field :name, :autofocus => true, :value => current_user.name, class: 'form-control', required: true, pattern: ".{4,}"
.form-group
= f.label :email
#controls
= f.text_field :email, :autofocus => true, :value => '', class: 'form-control', placeholder: '[email protected]'
.controls
= f.text_field :email, :autofocus => true, :value => '', class: 'form-control', placeholder: '[email protected]', required: true, type: "email"
%p.no-spam Es wird kein Spam versendet!
#actions
= f.submit 'Speichern und Fortfahren'
2 changes: 1 addition & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
devise_for :admin_users, ActiveAdmin::Devise.config
ActiveAdmin.routes(self)
root 'page#index'
devise_for :users, path: "api/users", defaults: { format: :json }, :controllers => { omniauth_callbacks: 'omniauth_callbacks' }, skip: [:confirmations]
devise_for :users, path: "api/users", defaults: { format: :json }, :controllers => { omniauth_callbacks: 'omniauth_callbacks', registrations: 'registrations' }, skip: [:confirmations]

as :user do
get '/users/confirm', to: 'confirmations#show', as: 'user_confirmation'
Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20140723153746_name_unique_to_users.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class NameUniqueToUsers < ActiveRecord::Migration
def change
add_index :users, :name, :unique => true
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20140715133004) do
ActiveRecord::Schema.define(version: 20140723153746) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -120,6 +120,7 @@
end

add_index "users", ["email"], :name => "index_users_on_email", :unique => true
add_index "users", ["name"], :name => "index_users_on_name", :unique => true
add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true

end

0 comments on commit 11b14ec

Please sign in to comment.