diff options
| -rw-r--r-- | users/app/assets/javascripts/users.js.coffee | 4 | ||||
| -rw-r--r-- | users/app/controllers/email_aliases_controller.rb | 39 | ||||
| -rw-r--r-- | users/app/controllers/users_controller.rb | 5 | ||||
| -rw-r--r-- | users/app/helpers/email_aliases_helper.rb | 11 | ||||
| -rw-r--r-- | users/app/models/email.rb | 17 | ||||
| -rw-r--r-- | users/app/models/local_email.rb | 15 | ||||
| -rw-r--r-- | users/app/models/user.rb | 73 | ||||
| -rw-r--r-- | users/app/views/emails/_email.html.haml | 4 | ||||
| -rw-r--r-- | users/app/views/users/_email_aliases.html.haml | 6 | ||||
| -rw-r--r-- | users/app/views/users/edit.html.haml | 1 | ||||
| -rw-r--r-- | users/config/locales/en.yml | 1 | ||||
| -rw-r--r-- | users/config/routes.rb | 4 | ||||
| -rw-r--r-- | users/test/unit/email_aliases_test.rb | 57 | ||||
| -rw-r--r-- | users/test/unit/email_test.rb | 39 | ||||
| -rw-r--r-- | users/test/unit/user_test.rb | 9 | 
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 | 
