Commit 9dbd7e5a authored by Douwe Maan's avatar Douwe Maan

Allow notification email to be set separately from primary email.

Closes #1932.
parent bc57ff0e
...@@ -60,6 +60,7 @@ v 7.8.0 ...@@ -60,6 +60,7 @@ v 7.8.0
- API: Access groups with their path (Julien Bianchi) - API: Access groups with their path (Julien Bianchi)
- Added link to milestone and keeping resource context on smaller viewports for issues and merge requests (Jason Blanchard) - Added link to milestone and keeping resource context on smaller viewports for issues and merge requests (Jason Blanchard)
- -
- Allow notification email to be set separately from primary email.
- -
- API: Add support for editing an existing project (Mika Mäenpää and Hannes Rosenögger) - API: Add support for editing an existing project (Mika Mäenpää and Hannes Rosenögger)
- -
......
...@@ -102,6 +102,9 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -102,6 +102,9 @@ class Admin::UsersController < Admin::ApplicationController
email = user.emails.find(params[:email_id]) email = user.emails.find(params[:email_id])
email.destroy email.destroy
user.set_notification_email
user.save if user.notification_email_changed?
respond_to do |format| respond_to do |format|
format.html { redirect_to :back, notice: "Successfully removed email." } format.html { redirect_to :back, notice: "Successfully removed email." }
format.js { render nothing: true } format.js { render nothing: true }
......
...@@ -18,6 +18,9 @@ class Profiles::EmailsController < ApplicationController ...@@ -18,6 +18,9 @@ class Profiles::EmailsController < ApplicationController
@email = current_user.emails.find(params[:id]) @email = current_user.emails.find(params[:id])
@email.destroy @email.destroy
current_user.set_notification_email
current_user.save if current_user.notification_email_changed?
respond_to do |format| respond_to do |format|
format.html { redirect_to profile_emails_url } format.html { redirect_to profile_emails_url }
format.js { render nothing: true } format.js { render nothing: true }
......
...@@ -2,6 +2,7 @@ class Profiles::NotificationsController < ApplicationController ...@@ -2,6 +2,7 @@ class Profiles::NotificationsController < ApplicationController
layout 'profile' layout 'profile'
def show def show
@user = current_user
@notification = current_user.notification @notification = current_user.notification
@project_members = current_user.project_members @project_members = current_user.project_members
@group_members = current_user.group_members @group_members = current_user.group_members
...@@ -11,8 +12,7 @@ class Profiles::NotificationsController < ApplicationController ...@@ -11,8 +12,7 @@ class Profiles::NotificationsController < ApplicationController
type = params[:notification_type] type = params[:notification_type]
@saved = if type == 'global' @saved = if type == 'global'
current_user.notification_level = params[:notification_level] current_user.update_attributes(user_params)
current_user.save
elsif type == 'group' elsif type == 'group'
users_group = current_user.group_members.find(params[:notification_id]) users_group = current_user.group_members.find(params[:notification_id])
users_group.notification_level = params[:notification_level] users_group.notification_level = params[:notification_level]
...@@ -22,5 +22,23 @@ class Profiles::NotificationsController < ApplicationController ...@@ -22,5 +22,23 @@ class Profiles::NotificationsController < ApplicationController
project_member.notification_level = params[:notification_level] project_member.notification_level = params[:notification_level]
project_member.save project_member.save
end end
respond_to do |format|
format.html do
if @saved
flash[:notice] = "Notification settings saved"
else
flash[:alert] = "Failed to save new settings"
end
redirect_to :back
end
format.js
end
end
def user_params
params.require(:user).permit(:notification_email, :notification_level)
end end
end end
...@@ -4,20 +4,20 @@ module Emails ...@@ -4,20 +4,20 @@ module Emails
@user = User.find(user_id) @user = User.find(user_id)
@target_url = user_url(@user) @target_url = user_url(@user)
@token = token @token = token
mail(to: @user.email, subject: subject("Account was created for you")) mail(to: @user.notification_email, subject: subject("Account was created for you"))
end end
def new_email_email(email_id) def new_email_email(email_id)
@email = Email.find(email_id) @email = Email.find(email_id)
@user = @email.user @user = @email.user
mail(to: @user.email, subject: subject("Email was added to your account")) mail(to: @user.notification_email, subject: subject("Email was added to your account"))
end end
def new_ssh_key_email(key_id) def new_ssh_key_email(key_id)
@key = Key.find(key_id) @key = Key.find(key_id)
@user = @key.user @user = @key.user
@target_url = user_url(@user) @target_url = user_url(@user)
mail(to: @user.email, subject: subject("SSH key was added to your account")) mail(to: @user.notification_email, subject: subject("SSH key was added to your account"))
end end
end end
end end
...@@ -12,7 +12,7 @@ module Emails ...@@ -12,7 +12,7 @@ module Emails
@user = User.find user_id @user = User.find user_id
@project = Project.find project_id @project = Project.find project_id
@target_url = project_url(@project) @target_url = project_url(@project)
mail(to: @user.email, mail(to: @user.notification_email,
subject: subject("Project was moved")) subject: subject("Project was moved"))
end end
......
...@@ -60,7 +60,7 @@ class Notify < ActionMailer::Base ...@@ -60,7 +60,7 @@ class Notify < ActionMailer::Base
# Returns a String containing the User's email address. # Returns a String containing the User's email address.
def recipient(recipient_id) def recipient(recipient_id)
if recipient = User.find(recipient_id) if recipient = User.find(recipient_id)
recipient.email recipient.notification_email
end end
end end
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
# website_url :string(255) default(""), not null # website_url :string(255) default(""), not null
# last_credential_check_at :datetime # last_credential_check_at :datetime
# github_access_token :string(255) # github_access_token :string(255)
# notification_email :string(255)
# #
require 'carrierwave/orm/activerecord' require 'carrierwave/orm/activerecord'
...@@ -114,6 +115,7 @@ class User < ActiveRecord::Base ...@@ -114,6 +115,7 @@ class User < ActiveRecord::Base
# #
validates :name, presence: true validates :name, presence: true
validates :email, presence: true, email: { strict_mode: true }, uniqueness: true validates :email, presence: true, email: { strict_mode: true }, uniqueness: true
validates :notification_email, presence: true, email: { strict_mode: true }
validates :bio, length: { maximum: 255 }, allow_blank: true validates :bio, length: { maximum: 255 }, allow_blank: true
validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 }
validates :username, validates :username,
...@@ -127,10 +129,12 @@ class User < ActiveRecord::Base ...@@ -127,10 +129,12 @@ class User < ActiveRecord::Base
validate :namespace_uniq, if: ->(user) { user.username_changed? } validate :namespace_uniq, if: ->(user) { user.username_changed? }
validate :avatar_type, if: ->(user) { user.avatar_changed? } validate :avatar_type, if: ->(user) { user.avatar_changed? }
validate :unique_email, if: ->(user) { user.email_changed? } validate :unique_email, if: ->(user) { user.email_changed? }
validate :owns_notification_email, if: ->(user) { user.notification_email_changed? }
validates :avatar, file_size: { maximum: 200.kilobytes.to_i } validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
before_validation :generate_password, on: :create before_validation :generate_password, on: :create
before_validation :sanitize_attrs before_validation :sanitize_attrs
before_validation :set_notification_email, if: ->(user) { user.email_changed? }
before_save :ensure_authentication_token before_save :ensure_authentication_token
after_save :ensure_namespace_correct after_save :ensure_namespace_correct
...@@ -286,6 +290,10 @@ class User < ActiveRecord::Base ...@@ -286,6 +290,10 @@ class User < ActiveRecord::Base
self.errors.add(:email, 'has already been taken') if Email.exists?(email: self.email) self.errors.add(:email, 'has already been taken') if Email.exists?(email: self.email)
end end
def owns_notification_email
self.errors.add(:notification_email, "is not an email you own") unless self.all_emails.include?(self.notification_email)
end
# Groups user has access to # Groups user has access to
def authorized_groups def authorized_groups
@authorized_groups ||= begin @authorized_groups ||= begin
...@@ -431,6 +439,12 @@ class User < ActiveRecord::Base ...@@ -431,6 +439,12 @@ class User < ActiveRecord::Base
end end
end end
def set_notification_email
if self.notification_email.blank? || !self.all_emails.include?(self.notification_email)
self.notification_email = self.email
end
end
def requires_ldap_check? def requires_ldap_check?
if !Gitlab.config.ldap.enabled if !Gitlab.config.ldap.enabled
false false
...@@ -504,6 +518,10 @@ class User < ActiveRecord::Base ...@@ -504,6 +518,10 @@ class User < ActiveRecord::Base
end end
end end
def all_emails
[self.email, *self.emails.map(&:email)]
end
def hook_attrs def hook_attrs
{ {
name: name, name: name,
......
...@@ -3,7 +3,11 @@ ...@@ -3,7 +3,11 @@
%p.light %p.light
Your Your
%b Primary Email %b Primary Email
will be used for account notifications, avatar detection and web based operations, such as edits and merges. will be used for avatar detection and web based operations, such as edits and merges.
%br
Your
%b Notification Email
will be used for account notifications.
%br %br
All email addresses will be used to identify your commits. All email addresses will be used to identify your commits.
......
%h3.page-title %h3.page-title
Notifications settings Notifications settings
%p.light %p.light
GitLab uses the email specified in your profile for notifications These are your global notification settings.
%hr %hr
= form_tag profile_notifications_path, method: :put, remote: true, class: 'update-notifications form-horizontal global-notifications-form' do
= form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications form-horizontal global-notifications-form' } do |f|
-if @user.errors.any?
%div.alert.alert-danger
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
= hidden_field_tag :notification_type, 'global' = hidden_field_tag :notification_type, 'global'
= label_tag :notification_level, 'Notification level', class: 'control-label' .form-group
= f.label :notification_email, class: "control-label"
.col-sm-10
= f.select :notification_email, @user.all_emails, { include_blank: false }, class: "form-control"
.form-group
= f.label :notification_level, class: 'control-label'
.col-sm-10 .col-sm-10
.radio .radio
= label_tag nil, class: '' do = f.label :notification_level, value: Notification::N_DISABLED do
= radio_button_tag :notification_level, Notification::N_DISABLED, @notification.disabled?, class: 'trigger-submit' = f.radio_button :notification_level, Notification::N_DISABLED
.level-title .level-title
Disabled Disabled
%p You will not get any notifications via email %p You will not get any notifications via email
.radio .radio
= label_tag nil, class: '' do = f.label :notification_level, value: Notification::N_MENTION do
= radio_button_tag :notification_level, Notification::N_MENTION, @notification.mention?, class: 'trigger-submit' = f.radio_button :notification_level, Notification::N_MENTION
.level-title .level-title
Mention Mention
%p You will receive notifications only for comments in which you were @mentioned %p You will receive notifications only for comments in which you were @mentioned
.radio .radio
= label_tag nil, class: '' do = f.label :notification_level, value: Notification::N_PARTICIPATING do
= radio_button_tag :notification_level, Notification::N_PARTICIPATING, @notification.participating?, class: 'trigger-submit' = f.radio_button :notification_level, Notification::N_PARTICIPATING
.level-title .level-title
Participating Participating
%p You will only receive notifications from related resources (e.g. from your commits or assigned issues) %p You will only receive notifications from related resources (e.g. from your commits or assigned issues)
.radio .radio
= label_tag nil, class: '' do = f.label :notification_level, value: Notification::N_WATCH do
= radio_button_tag :notification_level, Notification::N_WATCH, @notification.watch?, class: 'trigger-submit' = f.radio_button :notification_level, Notification::N_WATCH
.level-title .level-title
Watch Watch
%p You will receive all notifications from projects in which you participate %p You will receive all notifications from projects in which you participate
.form-actions
= f.submit 'Save changes', class: "btn btn-save"
.clearfix .clearfix
%hr %hr
.row.all-notifications .row.all-notifications
......
class AddNotificationEmailToUser < ActiveRecord::Migration
def up
add_column :users, :notification_email, :string
execute "UPDATE users SET notification_email = email"
end
def down
remove_column :users, :notification_email
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150125163100) do ActiveRecord::Schema.define(version: 20150206222854) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -436,6 +436,7 @@ ActiveRecord::Schema.define(version: 20150125163100) do ...@@ -436,6 +436,7 @@ ActiveRecord::Schema.define(version: 20150125163100) do
t.string "website_url", default: "", null: false t.string "website_url", default: "", null: false
t.string "github_access_token" t.string "github_access_token"
t.string "gitlab_access_token" t.string "gitlab_access_token"
t.string "notification_email"
end end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
......
...@@ -9,9 +9,14 @@ describe Notify do ...@@ -9,9 +9,14 @@ describe Notify do
let(:recipient) { create(:user, email: 'recipient@example.com') } let(:recipient) { create(:user, email: 'recipient@example.com') }
let(:project) { create(:project) } let(:project) { create(:project) }
before(:each) do
email = recipient.emails.create(email: "notifications@example.com")
recipient.update_attribute(:notification_email, email.email)
end
shared_examples 'a multiple recipients email' do shared_examples 'a multiple recipients email' do
it 'is sent to the given recipient' do it 'is sent to the given recipient' do
should deliver_to recipient.email should deliver_to recipient.notification_email
end end
end end
...@@ -441,7 +446,7 @@ describe Notify do ...@@ -441,7 +446,7 @@ describe Notify do
end end
it 'is sent to the given recipient' do it 'is sent to the given recipient' do
should deliver_to recipient.email should deliver_to recipient.notification_email
end end
it 'contains the message from the note' do it 'contains the message from the note' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment