summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--users/app/assets/javascripts/users.js.coffee4
-rw-r--r--users/app/controllers/email_aliases_controller.rb39
-rw-r--r--users/app/controllers/users_controller.rb5
-rw-r--r--users/app/helpers/email_aliases_helper.rb11
-rw-r--r--users/app/models/email.rb17
-rw-r--r--users/app/models/local_email.rb15
-rw-r--r--users/app/models/user.rb73
-rw-r--r--users/app/views/emails/_email.html.haml4
-rw-r--r--users/app/views/users/_email_aliases.html.haml6
-rw-r--r--users/app/views/users/edit.html.haml1
-rw-r--r--users/config/locales/en.yml1
-rw-r--r--users/config/routes.rb4
-rw-r--r--users/test/unit/email_aliases_test.rb57
-rw-r--r--users/test/unit/email_test.rb39
-rw-r--r--users/test/unit/user_test.rb9
15 files changed, 281 insertions, 4 deletions
diff --git a/users/app/assets/javascripts/users.js.coffee b/users/app/assets/javascripts/users.js.coffee
index 0595292..0c1fb55 100644
--- a/users/app/assets/javascripts/users.js.coffee
+++ b/users/app/assets/javascripts/users.js.coffee
@@ -32,3 +32,7 @@ $(document).ready ->
$('.user.form.change_password').submit srp.update
$('.user.form.change_password').submit preventDefault
$('.user.typeahead').typeahead({source: pollUsers});
+ $('a[data-toggle="tab"]').on('shown', ->
+ $(ClientSideValidations.selectors.forms).validate()
+ )
+
diff --git a/users/app/controllers/email_aliases_controller.rb b/users/app/controllers/email_aliases_controller.rb
new file mode 100644
index 0000000..751df85
--- /dev/null
+++ b/users/app/controllers/email_aliases_controller.rb
@@ -0,0 +1,39 @@
+class EmailAliasesController < ApplicationController
+
+ before_filter :fetch_user
+
+ respond_to :html
+
+ # get a list of email aliases for the given user?
+ def index
+ @aliases = @user.email_aliases
+ respond_with @aliases
+ end
+
+ def create
+ @alias = @user.add_email_alias(params[:email_alias])
+ flash[:notice] = t(:email_alias_created_successfully) unless @alias.errors
+ respond_with @alias, :location => edit_user_path(@user, :anchor => :email)
+ end
+
+ def update
+ @alias = @user.get_email_alias(params[:id])
+ @alias.set_email(params[:email_alias])
+ flash[:notice] = t(:email_alias_updated_successfully) unless @alias.errors
+ respond_with @alias, :location => edit_user_path(@user, :anchor => :email)
+ end
+
+ def destroy
+ @alias = @user.get_email_alias(params[:id])
+ flash[:notice] = t(:email_alias_destroyed_successfully)
+ @alias.destroy
+ redirect_to edit_user_path(@user, :anchor => :email)
+ end
+
+ protected
+
+ def fetch_user
+ @user = User.find_by_param(params[:user_id])
+ access_denied unless admin? or (@user == current_user)
+ end
+end
diff --git a/users/app/controllers/users_controller.rb b/users/app/controllers/users_controller.rb
index 4921a4a..811e8e5 100644
--- a/users/app/controllers/users_controller.rb
+++ b/users/app/controllers/users_controller.rb
@@ -30,8 +30,11 @@ class UsersController < ApplicationController
end
def update
- if @user.update_attributes(params[:user])
+ @user.attributes = params[:user]
+ if @user.changed? and @user.save
flash[:notice] = t(:user_updated_successfully)
+ else
+ flash[:error] = @user.errors.full_messages
end
respond_with @user, :location => edit_user_path(@user)
end
diff --git a/users/app/helpers/email_aliases_helper.rb b/users/app/helpers/email_aliases_helper.rb
new file mode 100644
index 0000000..b56b068
--- /dev/null
+++ b/users/app/helpers/email_aliases_helper.rb
@@ -0,0 +1,11 @@
+module EmailAliasesHelper
+
+ def email_alias_form(options = {})
+ simple_form_for [@user, EmailAlias.new()],
+ :html => {:class => "form-horizontal email-alias form"},
+ :validate => true do |f|
+ yield f
+ end
+ end
+
+end
diff --git a/users/app/models/email.rb b/users/app/models/email.rb
new file mode 100644
index 0000000..4b01838
--- /dev/null
+++ b/users/app/models/email.rb
@@ -0,0 +1,17 @@
+class Email
+ include CouchRest::Model::Embeddable
+
+ property :email, String
+
+ validates :email,
+ :format => { :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/, :message => "needs to be a valid email address"}
+
+ def initialize(attributes = nil, &block)
+ attributes = {:email => attributes} if attributes.is_a? String
+ super(attributes, &block)
+ end
+
+ def to_s
+ email
+ end
+end
diff --git a/users/app/models/local_email.rb b/users/app/models/local_email.rb
new file mode 100644
index 0000000..7cca4f4
--- /dev/null
+++ b/users/app/models/local_email.rb
@@ -0,0 +1,15 @@
+class LocalEmail < Email
+
+ validate :unique_on_server
+
+ def to_partial_path
+ "emails/email"
+ end
+
+ def unique_on_server
+ has_email = User.find_by_email_or_alias(email)
+ if has_email && has_email != self.base_doc
+ errors.add(:email, "has already been taken")
+ end
+ end
+end
diff --git a/users/app/models/user.rb b/users/app/models/user.rb
index 340ad9c..10f358d 100644
--- a/users/app/models/user.rb
+++ b/users/app/models/user.rb
@@ -3,11 +3,13 @@ class User < CouchRest::Model::Base
use_database :users
property :login, String, :accessible => true
- property :email, String, :accessible => true
- property :email_forward, String, :accessible => true
property :password_verifier, String, :accessible => true
property :password_salt, String, :accessible => true
+ property :email, String, :accessible => true
+ property :email_forward, String, :accessible => true
+ property :email_aliases, [LocalEmail]
+
validates :login, :password_salt, :password_verifier,
:presence => true
@@ -26,11 +28,48 @@ class User < CouchRest::Model::Base
:confirmation => true,
:format => { :with => /.{8}.*/, :message => "needs to be at least 8 characters long" }
+ # TODO: write a proper email validator to be used in the different places
+ validates :email,
+ :format => { :with => /\A(([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,}))?\Z/, :message => "needs to be a valid email address"}
+
+ validates :email_forward,
+ :format => { :with => /\A(([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,}))?\Z/, :message => "needs to be a valid email address"}
+
+ validate :no_duplicate_email_aliases
+
+ validate :email_aliases_differ_from_email
+
timestamps!
design do
view :by_login
view :by_created_at
+ view :by_email
+ view :by_email_alias,
+ :map => <<-EOJS
+ function(doc) {
+ if (doc.type != 'User') {
+ return;
+ }
+ doc.email_aliases.forEach(function(alias){
+ emit(alias.email, doc);
+ });
+ }
+ EOJS
+ view :by_email_or_alias,
+ :map => <<-EOJS
+ function(doc) {
+ if (doc.type != 'User') {
+ return;
+ }
+ if (doc.email) {
+ emit(doc.email, doc);
+ }
+ doc.email_aliases.forEach(function(alias){
+ emit(alias.email, doc);
+ });
+ }
+ EOJS
end
class << self
@@ -75,6 +114,36 @@ class User < CouchRest::Model::Base
APP_CONFIG['admins'].include? self.login
end
+ def add_email_alias(email)
+ email = LocalEmail.new(email) unless email.is_a? Email
+ email_aliases << email
+ end
+
+ # this currently only adds the first email address submitted.
+ # All the ui needs for now.
+ def email_aliases_attributes=(attrs)
+ if attrs && attrs.values.first
+ add_email_alias attrs.values.first
+ end
+ end
+
+ ##
+ # Validation Functions
+ ##
+
+ # TODO: How do we handle these errors?
+ def no_duplicate_email_aliases
+ if email_aliases.count != email_aliases.map(&:email).uniq.count
+ errors.add(:email_aliases, "include a duplicate")
+ end
+ end
+
+ def email_aliases_differ_from_email
+ if email_aliases.map(&:email).include?(email)
+ errors.add(:email_aliases, "include the original email address")
+ end
+ end
+
protected
def password
password_verifier
diff --git a/users/app/views/emails/_email.html.haml b/users/app/views/emails/_email.html.haml
new file mode 100644
index 0000000..f182ed9
--- /dev/null
+++ b/users/app/views/emails/_email.html.haml
@@ -0,0 +1,4 @@
+%li.pull-right
+ %code= email
+ %i.icon-remove
+.clearfix
diff --git a/users/app/views/users/_email_aliases.html.haml b/users/app/views/users/_email_aliases.html.haml
new file mode 100644
index 0000000..646480e
--- /dev/null
+++ b/users/app/views/users/_email_aliases.html.haml
@@ -0,0 +1,6 @@
+.span6
+ %ul.unstyled
+ =render @user.email_aliases
+.clearfix
+= f.simple_fields_for :email_aliases, Email.new do |e|
+ = e.input :email, :placeholder => "alias@#{request.domain}"
diff --git a/users/app/views/users/edit.html.haml b/users/app/views/users/edit.html.haml
index b33c19b..92ab71b 100644
--- a/users/app/views/users/edit.html.haml
+++ b/users/app/views/users/edit.html.haml
@@ -14,3 +14,4 @@
.tab-pane#email
= user_form_with 'email_field', :legend => :set_email_address
= user_form_with 'email_forward_field', :legend => :forward_email
+ = user_form_with 'email_aliases', :legend => :add_email_alias
diff --git a/users/config/locales/en.yml b/users/config/locales/en.yml
index fe7e824..d068e70 100644
--- a/users/config/locales/en.yml
+++ b/users/config/locales/en.yml
@@ -12,6 +12,7 @@ en:
set_email_address: "Set email address"
forward_email: "Forward email"
email_aliases: "Email aliases"
+ add_email_alias: "Add email alias"
user_updated_successfully: "Settings have been updated successfully."
user_created_successfully: "Successfully created your account."
diff --git a/users/config/routes.rb b/users/config/routes.rb
index 6de216f..3c5fb73 100644
--- a/users/config/routes.rb
+++ b/users/config/routes.rb
@@ -10,6 +10,8 @@ Rails.application.routes.draw do
resources :sessions, :only => [:new, :create, :update, :destroy]
get "signup" => "users#new", :as => "signup"
- resources :users
+ resources :users do
+ resources :email_aliases
+ end
end
diff --git a/users/test/unit/email_aliases_test.rb b/users/test/unit/email_aliases_test.rb
new file mode 100644
index 0000000..762aaea
--- /dev/null
+++ b/users/test/unit/email_aliases_test.rb
@@ -0,0 +1,57 @@
+require 'test_helper'
+
+class EmailAliasTest < ActiveSupport::TestCase
+
+ setup do
+ @attribs = User.valid_attributes_hash
+ User.find_by_login(@attribs[:login]).try(:destroy)
+ @user = User.new(@attribs)
+ end
+
+ test "no email aliases set in the beginning" do
+ assert_equal [], @user.email_aliases
+ end
+
+ test "adding email alias through params" do
+ email_alias = "valid_alias@domain.net"
+ @user.attributes = {:email_aliases_attributes => {"0" => {:email => email_alias}}}
+ assert @user.changed?
+ assert @user.save
+ assert_equal email_alias, @user.email_aliases.first.to_s
+ end
+
+ test "adding email alias directly" do
+ email_alias = "valid_alias@domain.net"
+ @user.add_email_alias(email_alias)
+ assert @user.changed?
+ assert @user.save
+ assert_equal email_alias, @user.reload.email_aliases.first.to_s
+ end
+
+ test "duplicated email aliases are invalid" do
+ email_alias = "valid_alias@domain.net"
+ @user.add_email_alias(email_alias)
+ @user.save
+ # add again
+ @user.add_email_alias(email_alias)
+ assert @user.changed?
+ assert !@user.valid?
+ end
+
+ test "email is invalid as email alias" do
+ email_alias = "valid_alias@domain.net"
+ @user.email = email_alias
+ @user.add_email_alias(email_alias)
+ assert @user.changed?
+ assert !@user.valid?
+ end
+
+ test "find user by email alias" do
+ email_alias = "valid_alias@domain.net"
+ @user.add_email_alias(email_alias)
+ assert @user.save
+ assert_equal @user, User.find_by_email_or_alias(email_alias)
+ assert_equal @user, User.find_by_email_alias(email_alias)
+ assert_nil User.find_by_email(email_alias)
+ end
+end
diff --git a/users/test/unit/email_test.rb b/users/test/unit/email_test.rb
new file mode 100644
index 0000000..1e216d6
--- /dev/null
+++ b/users/test/unit/email_test.rb
@@ -0,0 +1,39 @@
+require 'test_helper'
+
+class EmailTest < ActiveSupport::TestCase
+
+ setup do
+ # TODO build helper for this ... make_record(User)
+ @attribs = User.valid_attributes_hash
+ User.find_by_login(@attribs[:login]).try(:destroy)
+ @user = User.new(@attribs)
+ @attribs.merge!(:login => "other_user")
+ User.find_by_login(@attribs[:login]).try(:destroy)
+ @other_user = User.create(@attribs)
+ end
+
+ teardown do
+ @user.destroy if @user.persisted? # just in case
+ @other_user.destroy
+ end
+
+
+ test "email aliases need to be unique" do
+ email_alias = "valid_alias@domain.net"
+ @other_user.add_email_alias email_alias
+ @other_user.save
+ @user.add_email_alias email_alias
+ assert @user.changed?
+ assert !@user.save
+ # TODO handle errors
+ end
+
+ test "email aliases may not conflict with emails" do
+ email_alias = "valid_alias@domain.net"
+ @other_user.email = email_alias
+ @other_user.save
+ @user.add_email_alias email_alias
+ assert @user.changed?
+ assert !@user.save
+ end
+end
diff --git a/users/test/unit/user_test.rb b/users/test/unit/user_test.rb
index cce11c2..29f6a89 100644
--- a/users/test/unit/user_test.rb
+++ b/users/test/unit/user_test.rb
@@ -49,4 +49,13 @@ class UserTest < ActiveSupport::TestCase
assert_equal client_rnd, srp_session.aa
end
+ test "find user by email" do
+ email = "tryto@find.me"
+ @user.email = email
+ @user.save
+ assert_equal @user, User.find_by_email(email)
+ assert_equal @user, User.find_by_email_or_alias(email)
+ assert_nil User.find_by_email_alias(email)
+ end
+
end