@@ -0,0 +1,105 | |||
|
1 | # Redmine - project management software | |
|
2 | # Copyright (C) 2006-2015 Jean-Philippe Lang | |
|
3 | # | |
|
4 | # This program is free software; you can redistribute it and/or | |
|
5 | # modify it under the terms of the GNU General Public License | |
|
6 | # as published by the Free Software Foundation; either version 2 | |
|
7 | # of the License, or (at your option) any later version. | |
|
8 | # | |
|
9 | # This program is distributed in the hope that it will be useful, | |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
|
12 | # GNU General Public License for more details. | |
|
13 | # | |
|
14 | # You should have received a copy of the GNU General Public License | |
|
15 | # along with this program; if not, write to the Free Software | |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
|
17 | ||
|
18 | class EmailAddressesController < ApplicationController | |
|
19 | before_filter :find_user, :require_admin_or_current_user | |
|
20 | before_filter :find_email_address, :only => [:update, :destroy] | |
|
21 | ||
|
22 | def index | |
|
23 | @addresses = @user.email_addresses.order(:id).where(:is_default => false).to_a | |
|
24 | @address ||= EmailAddress.new | |
|
25 | end | |
|
26 | ||
|
27 | def create | |
|
28 | saved = false | |
|
29 | if @user.email_addresses.count <= Setting.max_additional_emails.to_i | |
|
30 | @address = EmailAddress.new(:user => @user, :is_default => false) | |
|
31 | attrs = params[:email_address] | |
|
32 | if attrs.is_a?(Hash) | |
|
33 | @address.address = attrs[:address].to_s | |
|
34 | end | |
|
35 | saved = @address.save | |
|
36 | end | |
|
37 | ||
|
38 | respond_to do |format| | |
|
39 | format.html { | |
|
40 | if saved | |
|
41 | redirect_to user_email_addresses_path(@user) | |
|
42 | else | |
|
43 | index | |
|
44 | render :action => 'index' | |
|
45 | end | |
|
46 | } | |
|
47 | format.js { | |
|
48 | @address = nil if saved | |
|
49 | index | |
|
50 | render :action => 'index' | |
|
51 | } | |
|
52 | end | |
|
53 | end | |
|
54 | ||
|
55 | def update | |
|
56 | if params[:notify].present? | |
|
57 | @address.notify = params[:notify].to_s | |
|
58 | end | |
|
59 | @address.save | |
|
60 | ||
|
61 | respond_to do |format| | |
|
62 | format.html { | |
|
63 | redirect_to user_email_addresses_path(@user) | |
|
64 | } | |
|
65 | format.js { | |
|
66 | @address = nil | |
|
67 | index | |
|
68 | render :action => 'index' | |
|
69 | } | |
|
70 | end | |
|
71 | end | |
|
72 | ||
|
73 | def destroy | |
|
74 | @address.destroy | |
|
75 | ||
|
76 | respond_to do |format| | |
|
77 | format.html { | |
|
78 | redirect_to user_email_addresses_path(@user) | |
|
79 | } | |
|
80 | format.js { | |
|
81 | @address = nil | |
|
82 | index | |
|
83 | render :action => 'index' | |
|
84 | } | |
|
85 | end | |
|
86 | end | |
|
87 | ||
|
88 | private | |
|
89 | ||
|
90 | def find_user | |
|
91 | @user = User.find(params[:user_id]) | |
|
92 | end | |
|
93 | ||
|
94 | def find_email_address | |
|
95 | @address = @user.email_addresses.where(:is_default => false).find(params[:id]) | |
|
96 | rescue ActiveRecord::RecordNotFound | |
|
97 | render_404 | |
|
98 | end | |
|
99 | ||
|
100 | def require_admin_or_current_user | |
|
101 | unless @user == User.current | |
|
102 | require_admin | |
|
103 | end | |
|
104 | end | |
|
105 | end |
@@ -0,0 +1,38 | |||
|
1 | # encoding: utf-8 | |
|
2 | # | |
|
3 | # Redmine - project management software | |
|
4 | # Copyright (C) 2006-2015 Jean-Philippe Lang | |
|
5 | # | |
|
6 | # This program is free software; you can redistribute it and/or | |
|
7 | # modify it under the terms of the GNU General Public License | |
|
8 | # as published by the Free Software Foundation; either version 2 | |
|
9 | # of the License, or (at your option) any later version. | |
|
10 | # | |
|
11 | # This program is distributed in the hope that it will be useful, | |
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
|
14 | # GNU General Public License for more details. | |
|
15 | # | |
|
16 | # You should have received a copy of the GNU General Public License | |
|
17 | # along with this program; if not, write to the Free Software | |
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
|
19 | ||
|
20 | module EmailAddressesHelper | |
|
21 | ||
|
22 | # Returns a link to enable or disable notifications for the address | |
|
23 | def toggle_email_address_notify_link(address) | |
|
24 | if address.notify? | |
|
25 | link_to image_tag('email.png'), | |
|
26 | user_email_address_path(address.user, address, :notify => '0'), | |
|
27 | :method => :put, | |
|
28 | :title => l(:label_disable_notifications), | |
|
29 | :remote => true | |
|
30 | else | |
|
31 | link_to image_tag('email_disabled.png'), | |
|
32 | user_email_address_path(address.user, address, :notify => '1'), | |
|
33 | :method => :put, | |
|
34 | :title => l(:label_enable_notifications), | |
|
35 | :remote => true | |
|
36 | end | |
|
37 | end | |
|
38 | end |
@@ -0,0 +1,54 | |||
|
1 | # Redmine - project management software | |
|
2 | # Copyright (C) 2006-2015 Jean-Philippe Lang | |
|
3 | # | |
|
4 | # This program is free software; you can redistribute it and/or | |
|
5 | # modify it under the terms of the GNU General Public License | |
|
6 | # as published by the Free Software Foundation; either version 2 | |
|
7 | # of the License, or (at your option) any later version. | |
|
8 | # | |
|
9 | # This program is distributed in the hope that it will be useful, | |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
|
12 | # GNU General Public License for more details. | |
|
13 | # | |
|
14 | # You should have received a copy of the GNU General Public License | |
|
15 | # along with this program; if not, write to the Free Software | |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
|
17 | ||
|
18 | class EmailAddress < ActiveRecord::Base | |
|
19 | belongs_to :user | |
|
20 | attr_protected :id | |
|
21 | ||
|
22 | after_update :destroy_tokens | |
|
23 | after_destroy :destroy_tokens | |
|
24 | ||
|
25 | validates_presence_of :address | |
|
26 | validates_format_of :address, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true | |
|
27 | validates_length_of :address, :maximum => User::MAIL_LENGTH_LIMIT, :allow_nil => true | |
|
28 | validates_uniqueness_of :address, :case_sensitive => false, | |
|
29 | :if => Proc.new {|email| email.address_changed? && email.address.present?} | |
|
30 | ||
|
31 | def address=(arg) | |
|
32 | write_attribute(:address, arg.to_s.strip) | |
|
33 | end | |
|
34 | ||
|
35 | def destroy | |
|
36 | if is_default? | |
|
37 | false | |
|
38 | else | |
|
39 | super | |
|
40 | end | |
|
41 | end | |
|
42 | ||
|
43 | private | |
|
44 | ||
|
45 | # Delete all outstanding password reset tokens on email change. | |
|
46 | # This helps to keep the account secure in case the associated email account | |
|
47 | # was compromised. | |
|
48 | def destroy_tokens | |
|
49 | if address_changed? || destroyed? | |
|
50 | tokens = ['recovery'] | |
|
51 | Token.where(:user_id => user_id, :action => tokens).delete_all | |
|
52 | end | |
|
53 | end | |
|
54 | end |
@@ -0,0 +1,26 | |||
|
1 | <% if @addresses.present? %> | |
|
2 | <table class="list email_addresses"> | |
|
3 | <% @addresses.each do |address| %> | |
|
4 | <tr class="<%= cycle("odd", "even") %>"> | |
|
5 | <td class="email"><%= address.address %></td> | |
|
6 | <td class="buttons"> | |
|
7 | <%= toggle_email_address_notify_link(address) %> | |
|
8 | <%= delete_link user_email_address_path(@user, address), :remote => true %> | |
|
9 | </td> | |
|
10 | </tr> | |
|
11 | <% end %> | |
|
12 | </table> | |
|
13 | <% end %> | |
|
14 | ||
|
15 | <% unless @addresses.size >= Setting.max_additional_emails.to_i %> | |
|
16 | <div> | |
|
17 | <%= form_for @address, :url => user_email_addresses_path(@user), :remote => true do |f| %> | |
|
18 | <p><%= l(:label_email_address_add) %></p> | |
|
19 | <%= error_messages_for @address %> | |
|
20 | <p> | |
|
21 | <%= f.text_field :address, :size => 40 %> | |
|
22 | <%= submit_tag l(:button_add) %> | |
|
23 | </p> | |
|
24 | <% end %> | |
|
25 | </div> | |
|
26 | <% end %> |
@@ -0,0 +1,3 | |||
|
1 | $('#ajax-modal').html('<%= escape_javascript(render :partial => 'email_addresses/index') %>'); | |
|
2 | showModal('ajax-modal', '600px', '<%= escape_javascript l(:label_email_address_plural) %>'); | |
|
3 | $('#email_address_address').focus(); |
@@ -0,0 +1,12 | |||
|
1 | class CreateEmailAddresses < ActiveRecord::Migration | |
|
2 | def change | |
|
3 | create_table :email_addresses do |t| | |
|
4 | t.column :user_id, :integer, :null => false | |
|
5 | t.column :address, :string, :null => false | |
|
6 | t.column :is_default, :boolean, :null => false, :default => false | |
|
7 | t.column :notify, :boolean, :null => false, :default => true | |
|
8 | t.column :created_on, :timestamp, :null => false | |
|
9 | t.column :updated_on, :timestamp, :null => false | |
|
10 | end | |
|
11 | end | |
|
12 | end |
@@ -0,0 +1,14 | |||
|
1 | class PopulateEmailAddresses < ActiveRecord::Migration | |
|
2 | def self.up | |
|
3 | t = EmailAddress.connection.quoted_true | |
|
4 | n = EmailAddress.connection.quoted_date(Time.now) | |
|
5 | ||
|
6 | sql = "INSERT INTO #{EmailAddress.table_name} (user_id, address, is_default, notify, created_on, updated_on)" + | |
|
7 | " SELECT id, mail, #{t}, #{t}, '#{n}', '#{n}' FROM #{User.table_name} WHERE type = 'User' ORDER BY id" | |
|
8 | EmailAddress.connection.execute(sql) | |
|
9 | end | |
|
10 | ||
|
11 | def self.down | |
|
12 | EmailAddress.delete_all | |
|
13 | end | |
|
14 | end |
@@ -0,0 +1,9 | |||
|
1 | class RemoveUsersMail < ActiveRecord::Migration | |
|
2 | def self.up | |
|
3 | remove_column :users, :mail | |
|
4 | end | |
|
5 | ||
|
6 | def self.down | |
|
7 | raise IrreversibleMigration | |
|
8 | end | |
|
9 | end |
@@ -0,0 +1,9 | |||
|
1 | class AddEmailAddressesUserIdIndex < ActiveRecord::Migration | |
|
2 | def up | |
|
3 | add_index :email_addresses, :user_id | |
|
4 | end | |
|
5 | ||
|
6 | def down | |
|
7 | remove_index :email_addresses, :user_id | |
|
8 | end | |
|
9 | end |
|
1 | NO CONTENT: new file 100644, binary diff hidden |
|
1 | NO CONTENT: new file 100644, binary diff hidden |
|
1 | NO CONTENT: new file 100644, binary diff hidden |
@@ -0,0 +1,57 | |||
|
1 | --- | |
|
2 | email_address_001: | |
|
3 | id: 1 | |
|
4 | user_id: 1 | |
|
5 | address: admin@somenet.foo | |
|
6 | is_default: true | |
|
7 | created_on: 2006-07-19 19:34:07 +02:00 | |
|
8 | updated_on: 2006-07-19 19:34:07 +02:00 | |
|
9 | email_address_002: | |
|
10 | id: 2 | |
|
11 | user_id: 2 | |
|
12 | address: jsmith@somenet.foo | |
|
13 | is_default: true | |
|
14 | created_on: 2006-07-19 19:34:07 +02:00 | |
|
15 | updated_on: 2006-07-19 19:34:07 +02:00 | |
|
16 | email_address_003: | |
|
17 | id: 3 | |
|
18 | user_id: 3 | |
|
19 | address: dlopper@somenet.foo | |
|
20 | is_default: true | |
|
21 | created_on: 2006-07-19 19:34:07 +02:00 | |
|
22 | updated_on: 2006-07-19 19:34:07 +02:00 | |
|
23 | email_address_004: | |
|
24 | id: 4 | |
|
25 | user_id: 4 | |
|
26 | address: rhill@somenet.foo | |
|
27 | is_default: true | |
|
28 | created_on: 2006-07-19 19:34:07 +02:00 | |
|
29 | updated_on: 2006-07-19 19:34:07 +02:00 | |
|
30 | email_address_005: | |
|
31 | id: 5 | |
|
32 | user_id: 5 | |
|
33 | address: dlopper2@somenet.foo | |
|
34 | is_default: true | |
|
35 | created_on: 2006-07-19 19:34:07 +02:00 | |
|
36 | updated_on: 2006-07-19 19:34:07 +02:00 | |
|
37 | email_address_007: | |
|
38 | id: 7 | |
|
39 | user_id: 7 | |
|
40 | address: someone@foo.bar | |
|
41 | is_default: true | |
|
42 | created_on: 2006-07-19 19:34:07 +02:00 | |
|
43 | updated_on: 2006-07-19 19:34:07 +02:00 | |
|
44 | email_address_008: | |
|
45 | id: 8 | |
|
46 | user_id: 8 | |
|
47 | address: miscuser8@foo.bar | |
|
48 | is_default: true | |
|
49 | created_on: 2006-07-19 19:34:07 +02:00 | |
|
50 | updated_on: 2006-07-19 19:34:07 +02:00 | |
|
51 | email_address_009: | |
|
52 | id: 9 | |
|
53 | user_id: 9 | |
|
54 | address: miscuser9@foo.bar | |
|
55 | is_default: true | |
|
56 | created_on: 2006-07-19 19:34:07 +02:00 | |
|
57 | updated_on: 2006-07-19 19:34:07 +02:00 |
@@ -0,0 +1,144 | |||
|
1 | # Redmine - project management software | |
|
2 | # Copyright (C) 2006-2015 Jean-Philippe Lang | |
|
3 | # | |
|
4 | # This program is free software; you can redistribute it and/or | |
|
5 | # modify it under the terms of the GNU General Public License | |
|
6 | # as published by the Free Software Foundation; either version 2 | |
|
7 | # of the License, or (at your option) any later version. | |
|
8 | # | |
|
9 | # This program is distributed in the hope that it will be useful, | |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
|
12 | # GNU General Public License for more details. | |
|
13 | # | |
|
14 | # You should have received a copy of the GNU General Public License | |
|
15 | # along with this program; if not, write to the Free Software | |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
|
17 | ||
|
18 | require File.expand_path('../../test_helper', __FILE__) | |
|
19 | ||
|
20 | class EmailAddressesControllerTest < ActionController::TestCase | |
|
21 | fixtures :users, :email_addresses | |
|
22 | ||
|
23 | def setup | |
|
24 | User.current = nil | |
|
25 | end | |
|
26 | ||
|
27 | def test_index_with_no_additional_emails | |
|
28 | @request.session[:user_id] = 2 | |
|
29 | get :index, :user_id => 2 | |
|
30 | assert_response :success | |
|
31 | assert_template 'index' | |
|
32 | end | |
|
33 | ||
|
34 | def test_index_with_additional_emails | |
|
35 | @request.session[:user_id] = 2 | |
|
36 | EmailAddress.create!(:user_id => 2, :address => 'another@somenet.foo') | |
|
37 | ||
|
38 | get :index, :user_id => 2 | |
|
39 | assert_response :success | |
|
40 | assert_template 'index' | |
|
41 | assert_select '.email', :text => 'another@somenet.foo' | |
|
42 | end | |
|
43 | ||
|
44 | def test_index_with_additional_emails_as_js | |
|
45 | @request.session[:user_id] = 2 | |
|
46 | EmailAddress.create!(:user_id => 2, :address => 'another@somenet.foo') | |
|
47 | ||
|
48 | xhr :get, :index, :user_id => 2 | |
|
49 | assert_response :success | |
|
50 | assert_template 'index' | |
|
51 | assert_include 'another@somenet.foo', response.body | |
|
52 | end | |
|
53 | ||
|
54 | def test_index_by_admin_should_be_allowed | |
|
55 | @request.session[:user_id] = 1 | |
|
56 | get :index, :user_id => 2 | |
|
57 | assert_response :success | |
|
58 | assert_template 'index' | |
|
59 | end | |
|
60 | ||
|
61 | def test_index_by_another_user_should_be_denied | |
|
62 | @request.session[:user_id] = 3 | |
|
63 | get :index, :user_id => 2 | |
|
64 | assert_response 403 | |
|
65 | end | |
|
66 | ||
|
67 | def test_create | |
|
68 | @request.session[:user_id] = 2 | |
|
69 | assert_difference 'EmailAddress.count' do | |
|
70 | post :create, :user_id => 2, :email_address => {:address => 'another@somenet.foo'} | |
|
71 | assert_response 302 | |
|
72 | assert_redirected_to '/users/2/email_addresses' | |
|
73 | end | |
|
74 | email = EmailAddress.order('id DESC').first | |
|
75 | assert_equal 2, email.user_id | |
|
76 | assert_equal 'another@somenet.foo', email.address | |
|
77 | end | |
|
78 | ||
|
79 | def test_create_as_js | |
|
80 | @request.session[:user_id] = 2 | |
|
81 | assert_difference 'EmailAddress.count' do | |
|
82 | xhr :post, :create, :user_id => 2, :email_address => {:address => 'another@somenet.foo'} | |
|
83 | assert_response 200 | |
|
84 | end | |
|
85 | end | |
|
86 | ||
|
87 | def test_create_with_failure | |
|
88 | @request.session[:user_id] = 2 | |
|
89 | assert_no_difference 'EmailAddress.count' do | |
|
90 | post :create, :user_id => 2, :email_address => {:address => 'invalid'} | |
|
91 | assert_response 200 | |
|
92 | end | |
|
93 | end | |
|
94 | ||
|
95 | def test_update | |
|
96 | @request.session[:user_id] = 2 | |
|
97 | email = EmailAddress.create!(:user_id => 2, :address => 'another@somenet.foo') | |
|
98 | ||
|
99 | put :update, :user_id => 2, :id => email.id, :notify => '0' | |
|
100 | assert_response 302 | |
|
101 | ||
|
102 | assert_equal false, email.reload.notify | |
|
103 | end | |
|
104 | ||
|
105 | def test_update_as_js | |
|
106 | @request.session[:user_id] = 2 | |
|
107 | email = EmailAddress.create!(:user_id => 2, :address => 'another@somenet.foo') | |
|
108 | ||
|
109 | xhr :put, :update, :user_id => 2, :id => email.id, :notify => '0' | |
|
110 | assert_response 200 | |
|
111 | ||
|
112 | assert_equal false, email.reload.notify | |
|
113 | end | |
|
114 | ||
|
115 | def test_destroy | |
|
116 | @request.session[:user_id] = 2 | |
|
117 | email = EmailAddress.create!(:user_id => 2, :address => 'another@somenet.foo') | |
|
118 | ||
|
119 | assert_difference 'EmailAddress.count', -1 do | |
|
120 | delete :destroy, :user_id => 2, :id => email.id | |
|
121 | assert_response 302 | |
|
122 | assert_redirected_to '/users/2/email_addresses' | |
|
123 | end | |
|
124 | end | |
|
125 | ||
|
126 | def test_destroy_as_js | |
|
127 | @request.session[:user_id] = 2 | |
|
128 | email = EmailAddress.create!(:user_id => 2, :address => 'another@somenet.foo') | |
|
129 | ||
|
130 | assert_difference 'EmailAddress.count', -1 do | |
|
131 | xhr :delete, :destroy, :user_id => 2, :id => email.id | |
|
132 | assert_response 200 | |
|
133 | end | |
|
134 | end | |
|
135 | ||
|
136 | def test_should_not_destroy_default | |
|
137 | @request.session[:user_id] = 2 | |
|
138 | ||
|
139 | assert_no_difference 'EmailAddress.count' do | |
|
140 | delete :destroy, :user_id => 2, :id => User.find(2).email_address.id | |
|
141 | assert_response 404 | |
|
142 | end | |
|
143 | end | |
|
144 | end |
@@ -41,7 +41,7 class UsersController < ApplicationController | |||
|
41 | 41 | |
|
42 | 42 | @status = params[:status] || 1 |
|
43 | 43 | |
|
44 | scope = User.logged.status(@status) | |
|
44 | scope = User.logged.status(@status).preload(:email_address) | |
|
45 | 45 | scope = scope.like(params[:name]) if params[:name].present? |
|
46 | 46 | scope = scope.in_group(params[:group_id]) if params[:group_id].present? |
|
47 | 47 |
@@ -42,6 +42,12 module UsersHelper | |||
|
42 | 42 | end |
|
43 | 43 | end |
|
44 | 44 | |
|
45 | def additional_emails_link(user) | |
|
46 | if user.email_addresses.count > 1 || Setting.max_additional_emails.to_i > 0 | |
|
47 | link_to l(:label_email_address_plural), user_email_addresses_path(@user), :class => 'icon icon-email-add', :remote => true | |
|
48 | end | |
|
49 | end | |
|
50 | ||
|
45 | 51 | def user_settings_tabs |
|
46 | 52 | tabs = [{:name => 'general', :partial => 'users/general', :label => :label_general}, |
|
47 | 53 | {:name => 'memberships', :partial => 'users/memberships', :label => :label_project_plural} |
@@ -60,6 +60,10 class Document < ActiveRecord::Base | |||
|
60 | 60 | @updated_on |
|
61 | 61 | end |
|
62 | 62 | |
|
63 | def notified_users | |
|
64 | project.notified_users.reject {|user| !visible?(user)} | |
|
65 | end | |
|
66 | ||
|
63 | 67 | private |
|
64 | 68 | |
|
65 | 69 | def send_notification |
@@ -306,7 +306,7 class MailHandler < ActionMailer::Base | |||
|
306 | 306 | if user.allowed_to?("add_#{obj.class.name.underscore}_watchers".to_sym, obj.project) |
|
307 | 307 | addresses = [email.to, email.cc].flatten.compact.uniq.collect {|a| a.strip.downcase} |
|
308 | 308 | unless addresses.empty? |
|
309 |
User.active. |
|
|
309 | User.active.having_mail(addresses).each do |w| | |
|
310 | 310 | obj.add_watcher(w) |
|
311 | 311 | end |
|
312 | 312 | end |
@@ -39,8 +39,8 class Mailer < ActionMailer::Base | |||
|
39 | 39 | @issue = issue |
|
40 | 40 | @users = to_users + cc_users |
|
41 | 41 | @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue) |
|
42 |
mail :to => to_users |
|
|
43 |
:cc => cc_users |
|
|
42 | mail :to => to_users, | |
|
43 | :cc => cc_users, | |
|
44 | 44 | :subject => "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}" |
|
45 | 45 | end |
|
46 | 46 | |
@@ -71,8 +71,8 class Mailer < ActionMailer::Base | |||
|
71 | 71 | @journal = journal |
|
72 | 72 | @journal_details = journal.visible_details(@users.first) |
|
73 | 73 | @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue, :anchor => "change-#{journal.id}") |
|
74 |
mail :to => to_users |
|
|
75 |
:cc => cc_users |
|
|
74 | mail :to => to_users, | |
|
75 | :cc => cc_users, | |
|
76 | 76 | :subject => s |
|
77 | 77 | end |
|
78 | 78 | |
@@ -95,7 +95,7 class Mailer < ActionMailer::Base | |||
|
95 | 95 | @issues_url = url_for(:controller => 'issues', :action => 'index', |
|
96 | 96 | :set_filter => 1, :assigned_to_id => user.id, |
|
97 | 97 | :sort => 'due_date:asc') |
|
98 |
mail :to => user |
|
|
98 | mail :to => user, | |
|
99 | 99 | :subject => l(:mail_subject_reminder, :count => issues.size, :days => days) |
|
100 | 100 | end |
|
101 | 101 | |
@@ -109,7 +109,7 class Mailer < ActionMailer::Base | |||
|
109 | 109 | @author = User.current |
|
110 | 110 | @document = document |
|
111 | 111 | @document_url = url_for(:controller => 'documents', :action => 'show', :id => document) |
|
112 |
mail :to => document. |
|
|
112 | mail :to => document.notified_users, | |
|
113 | 113 | :subject => "[#{document.project.name}] #{l(:label_document_new)}: #{document.title}" |
|
114 | 114 | end |
|
115 | 115 | |
@@ -127,15 +127,15 class Mailer < ActionMailer::Base | |||
|
127 | 127 | when 'Project' |
|
128 | 128 | added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container) |
|
129 | 129 | added_to = "#{l(:label_project)}: #{container}" |
|
130 |
recipients = container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)} |
|
|
130 | recipients = container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)} | |
|
131 | 131 | when 'Version' |
|
132 | 132 | added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container.project) |
|
133 | 133 | added_to = "#{l(:label_version)}: #{container.name}" |
|
134 |
recipients = container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)} |
|
|
134 | recipients = container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)} | |
|
135 | 135 | when 'Document' |
|
136 | 136 | added_to_url = url_for(:controller => 'documents', :action => 'show', :id => container.id) |
|
137 | 137 | added_to = "#{l(:label_document)}: #{container.title}" |
|
138 |
recipients = container. |
|
|
138 | recipients = container.notified_users | |
|
139 | 139 | end |
|
140 | 140 | redmine_headers 'Project' => container.project.identifier |
|
141 | 141 | @attachments = attachments |
@@ -157,8 +157,8 class Mailer < ActionMailer::Base | |||
|
157 | 157 | references news |
|
158 | 158 | @news = news |
|
159 | 159 | @news_url = url_for(:controller => 'news', :action => 'show', :id => news) |
|
160 |
mail :to => news. |
|
|
161 |
:cc => news. |
|
|
160 | mail :to => news.notified_users, | |
|
161 | :cc => news.notified_watchers_for_added_news, | |
|
162 | 162 | :subject => "[#{news.project.name}] #{l(:label_news)}: #{news.title}" |
|
163 | 163 | end |
|
164 | 164 | |
@@ -176,8 +176,8 class Mailer < ActionMailer::Base | |||
|
176 | 176 | @news = news |
|
177 | 177 | @comment = comment |
|
178 | 178 | @news_url = url_for(:controller => 'news', :action => 'show', :id => news) |
|
179 |
mail :to => news. |
|
|
180 |
:cc => news.watcher |
|
|
179 | mail :to => news.notified_users, | |
|
180 | :cc => news.notified_watchers, | |
|
181 | 181 | :subject => "Re: [#{news.project.name}] #{l(:label_news)}: #{news.title}" |
|
182 | 182 | end |
|
183 | 183 | |
@@ -192,8 +192,8 class Mailer < ActionMailer::Base | |||
|
192 | 192 | @author = message.author |
|
193 | 193 | message_id message |
|
194 | 194 | references message.root |
|
195 |
recipients = message. |
|
|
196 |
cc = ((message.root.watcher |
|
|
195 | recipients = message.notified_users | |
|
196 | cc = ((message.root.notified_watchers + message.board.notified_watchers).uniq - recipients) | |
|
197 | 197 | @message = message |
|
198 | 198 | @message_url = url_for(message.event_url) |
|
199 | 199 | mail :to => recipients, |
@@ -211,8 +211,8 class Mailer < ActionMailer::Base | |||
|
211 | 211 | 'Wiki-Page-Id' => wiki_content.page.id |
|
212 | 212 | @author = wiki_content.author |
|
213 | 213 | message_id wiki_content |
|
214 |
recipients = wiki_content. |
|
|
215 |
cc = wiki_content.page.wiki.watcher |
|
|
214 | recipients = wiki_content.notified_users | |
|
215 | cc = wiki_content.page.wiki.notified_watchers - recipients | |
|
216 | 216 | @wiki_content = wiki_content |
|
217 | 217 | @wiki_content_url = url_for(:controller => 'wiki', :action => 'show', |
|
218 | 218 | :project_id => wiki_content.project, |
@@ -232,8 +232,8 class Mailer < ActionMailer::Base | |||
|
232 | 232 | 'Wiki-Page-Id' => wiki_content.page.id |
|
233 | 233 | @author = wiki_content.author |
|
234 | 234 | message_id wiki_content |
|
235 |
recipients = wiki_content. |
|
|
236 |
cc = wiki_content.page.wiki.watcher |
|
|
235 | recipients = wiki_content.notified_users | |
|
236 | cc = wiki_content.page.wiki.notified_watchers + wiki_content.page.notified_watchers - recipients | |
|
237 | 237 | @wiki_content = wiki_content |
|
238 | 238 | @wiki_content_url = url_for(:controller => 'wiki', :action => 'show', |
|
239 | 239 | :project_id => wiki_content.project, |
@@ -267,7 +267,7 class Mailer < ActionMailer::Base | |||
|
267 | 267 | # Mailer.account_activation_request(user).deliver => sends an email to all active administrators |
|
268 | 268 | def account_activation_request(user) |
|
269 | 269 | # Send the email to all active administrators |
|
270 |
recipients = User.active.where(:admin => true) |
|
|
270 | recipients = User.active.where(:admin => true) | |
|
271 | 271 | @user = user |
|
272 | 272 | @url = url_for(:controller => 'users', :action => 'index', |
|
273 | 273 | :status => User::STATUS_REGISTERED, |
@@ -378,12 +378,20 class Mailer < ActionMailer::Base | |||
|
378 | 378 | 'From' => Setting.mail_from, |
|
379 | 379 | 'List-Id' => "<#{Setting.mail_from.to_s.gsub('@', '.')}>" |
|
380 | 380 | |
|
381 | # Replaces users with their email addresses | |
|
382 | [:to, :cc, :bcc].each do |key| | |
|
383 | if headers[key].present? | |
|
384 | headers[key] = self.class.email_addresses(headers[key]) | |
|
385 | end | |
|
386 | end | |
|
387 | ||
|
381 | 388 | # Removes the author from the recipients and cc |
|
382 | 389 | # if the author does not want to receive notifications |
|
383 | 390 | # about what the author do |
|
384 | 391 | if @author && @author.logged? && @author.pref.no_self_notified |
|
385 | headers[:to].delete(@author.mail) if headers[:to].is_a?(Array) | |
|
386 |
headers[: |
|
|
392 | addresses = @author.mails | |
|
393 | headers[:to] -= addresses if headers[:to].is_a?(Array) | |
|
394 | headers[:cc] -= addresses if headers[:cc].is_a?(Array) | |
|
387 | 395 | end |
|
388 | 396 | |
|
389 | 397 | if @author && @author.logged? |
@@ -447,6 +455,25 class Mailer < ActionMailer::Base | |||
|
447 | 455 | end |
|
448 | 456 | end |
|
449 | 457 | |
|
458 | # Returns an array of email addresses to notify by | |
|
459 | # replacing users in arg with their notified email addresses | |
|
460 | # | |
|
461 | # Example: | |
|
462 | # Mailer.email_addresses(users) | |
|
463 | # => ["foo@example.net", "bar@example.net"] | |
|
464 | def self.email_addresses(arg) | |
|
465 | arr = Array.wrap(arg) | |
|
466 | mails = arr.reject {|a| a.is_a? Principal} | |
|
467 | users = arr - mails | |
|
468 | if users.any? | |
|
469 | mails += EmailAddress. | |
|
470 | where(:user_id => users.map(&:id)). | |
|
471 | where("is_default = ? OR notify = ?", true, true). | |
|
472 | pluck(:address) | |
|
473 | end | |
|
474 | mails | |
|
475 | end | |
|
476 | ||
|
450 | 477 | private |
|
451 | 478 | |
|
452 | 479 | # Appends a Redmine header field (name is prepended with 'X-Redmine-') |
@@ -103,6 +103,10 class Message < ActiveRecord::Base | |||
|
103 | 103 | usr && usr.logged? && (usr.allowed_to?(:delete_messages, project) || (self.author == usr && usr.allowed_to?(:delete_own_messages, project))) |
|
104 | 104 | end |
|
105 | 105 | |
|
106 | def notified_users | |
|
107 | project.notified_users.reject {|user| !visible?(user)} | |
|
108 | end | |
|
109 | ||
|
106 | 110 | private |
|
107 | 111 | |
|
108 | 112 | def add_author_as_watcher |
@@ -54,20 +54,29 class News < ActiveRecord::Base | |||
|
54 | 54 | user.allowed_to?(:comment_news, project) |
|
55 | 55 | end |
|
56 | 56 | |
|
57 | def notified_users | |
|
58 | project.users.select {|user| user.notify_about?(self) && user.allowed_to?(:view_news, project)} | |
|
59 | end | |
|
60 | ||
|
57 | 61 | def recipients |
|
58 | project.users.select {|user| user.notify_about?(self) && user.allowed_to?(:view_news, project)}.map(&:mail) | |
|
62 | notified_users.map(&:mail) | |
|
59 | 63 | end |
|
60 | 64 | |
|
61 |
# Returns the |
|
|
62 |
def |
|
|
63 |
|
|
|
65 | # Returns the users that should be cc'd when a new news is added | |
|
66 | def notified_watchers_for_added_news | |
|
67 | watchers = [] | |
|
64 | 68 | if m = project.enabled_module('news') |
|
65 |
|
|
|
69 | watchers = m.notified_watchers | |
|
66 | 70 | unless project.is_public? |
|
67 |
|
|
|
71 | watchers = watchers.select {|user| project.users.include?(user)} | |
|
68 | 72 | end |
|
69 | 73 | end |
|
70 | cc.map(&:mail) | |
|
74 | watchers | |
|
75 | end | |
|
76 | ||
|
77 | # Returns the email addresses that should be cc'd when a new news is added | |
|
78 | def cc_for_added_news | |
|
79 | notified_watchers_for_added_news.map(&:mail) | |
|
71 | 80 | end |
|
72 | 81 | |
|
73 | 82 | # returns latest news for projects visible by user |
@@ -68,7 +68,8 class Principal < ActiveRecord::Base | |||
|
68 | 68 | where({}) |
|
69 | 69 | else |
|
70 | 70 | pattern = "%#{q}%" |
|
71 |
sql = %w(login firstname lastname |
|
|
71 | sql = %w(login firstname lastname).map {|column| "LOWER(#{table_name}.#{column}) LIKE LOWER(:p)"}.join(" OR ") | |
|
72 | sql << " OR #{table_name}.id IN (SELECT user_id FROM #{EmailAddress.table_name} WHERE LOWER(address) LIKE LOWER(:p))" | |
|
72 | 73 | params = {:p => pattern} |
|
73 | 74 | if q =~ /^(.+)\s+(.+)$/ |
|
74 | 75 | a, b = "#{$1}%", "#{$2}%" |
@@ -108,6 +109,14 class Principal < ActiveRecord::Base | |||
|
108 | 109 | to_s |
|
109 | 110 | end |
|
110 | 111 | |
|
112 | def mail=(*args) | |
|
113 | nil | |
|
114 | end | |
|
115 | ||
|
116 | def mail | |
|
117 | nil | |
|
118 | end | |
|
119 | ||
|
111 | 120 | def visible?(user=User.current) |
|
112 | 121 | Principal.visible(user).where(:id => id).first == self |
|
113 | 122 | end |
@@ -145,7 +154,6 class Principal < ActiveRecord::Base | |||
|
145 | 154 | self.hashed_password ||= '' |
|
146 | 155 | self.firstname ||= '' |
|
147 | 156 | self.lastname ||= '' |
|
148 | self.mail ||= '' | |
|
149 | 157 | true |
|
150 | 158 | end |
|
151 | 159 | end |
@@ -81,6 +81,8 class User < Principal | |||
|
81 | 81 | has_one :preference, :dependent => :destroy, :class_name => 'UserPreference' |
|
82 | 82 | has_one :rss_token, lambda {where "action='feeds'"}, :class_name => 'Token' |
|
83 | 83 | has_one :api_token, lambda {where "action='api'"}, :class_name => 'Token' |
|
84 | has_one :email_address, lambda {where :is_default => true}, :autosave => true | |
|
85 | has_many :email_addresses, :dependent => :delete_all | |
|
84 | 86 | belongs_to :auth_source |
|
85 | 87 | |
|
86 | 88 | scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") } |
@@ -96,15 +98,12 class User < Principal | |||
|
96 | 98 | LOGIN_LENGTH_LIMIT = 60 |
|
97 | 99 | MAIL_LENGTH_LIMIT = 60 |
|
98 | 100 | |
|
99 |
validates_presence_of :login, :firstname, :lastname |
|
|
101 | validates_presence_of :login, :firstname, :lastname, :if => Proc.new { |user| !user.is_a?(AnonymousUser) } | |
|
100 | 102 | validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, :case_sensitive => false |
|
101 | validates_uniqueness_of :mail, :if => Proc.new { |user| user.mail_changed? && user.mail.present? }, :case_sensitive => false | |
|
102 | 103 | # Login must contain letters, numbers, underscores only |
|
103 | 104 | validates_format_of :login, :with => /\A[a-z0-9_\-@\.]*\z/i |
|
104 | 105 | validates_length_of :login, :maximum => LOGIN_LENGTH_LIMIT |
|
105 | 106 | validates_length_of :firstname, :lastname, :maximum => 30 |
|
106 | validates_format_of :mail, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true | |
|
107 | validates_length_of :mail, :maximum => MAIL_LENGTH_LIMIT, :allow_nil => true | |
|
108 | 107 | validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true |
|
109 | 108 | validate :validate_password_length |
|
110 | 109 | validate do |
@@ -113,6 +112,7 class User < Principal | |||
|
113 | 112 | end |
|
114 | 113 | end |
|
115 | 114 | |
|
115 | before_validation :instantiate_email_address | |
|
116 | 116 | before_create :set_mail_notification |
|
117 | 117 | before_save :generate_password_if_needed, :update_hashed_password |
|
118 | 118 | before_destroy :remove_references_before_destroy |
@@ -127,6 +127,14 class User < Principal | |||
|
127 | 127 | where("#{User.table_name}.id NOT IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id) |
|
128 | 128 | } |
|
129 | 129 | scope :sorted, lambda { order(*User.fields_for_order_statement)} |
|
130 | scope :having_mail, lambda {|arg| | |
|
131 | addresses = Array.wrap(arg).map {|a| a.to_s.downcase} | |
|
132 | if addresses.any? | |
|
133 | joins(:email_addresses).where("LOWER(address) IN (?)", addresses).uniq | |
|
134 | else | |
|
135 | none | |
|
136 | end | |
|
137 | } | |
|
130 | 138 | |
|
131 | 139 | def set_mail_notification |
|
132 | 140 | self.mail_notification = Setting.default_notification_option if self.mail_notification.blank? |
@@ -152,8 +160,21 class User < Principal | |||
|
152 | 160 | base_reload(*args) |
|
153 | 161 | end |
|
154 | 162 | |
|
163 | def mail | |
|
164 | email_address.try(:address) | |
|
165 | end | |
|
166 | ||
|
155 | 167 | def mail=(arg) |
|
156 | write_attribute(:mail, arg.to_s.strip) | |
|
168 | email = email_address || build_email_address | |
|
169 | email.address = arg | |
|
170 | end | |
|
171 | ||
|
172 | def mail_changed? | |
|
173 | email_address.try(:address_changed?) | |
|
174 | end | |
|
175 | ||
|
176 | def mails | |
|
177 | email_addresses.pluck(:address) | |
|
157 | 178 | end |
|
158 | 179 | |
|
159 | 180 | def self.find_or_initialize_by_identity_url(url) |
@@ -421,7 +442,7 class User < Principal | |||
|
421 | 442 | |
|
422 | 443 | # Makes find_by_mail case-insensitive |
|
423 | 444 | def self.find_by_mail(mail) |
|
424 | where("LOWER(mail) = ?", mail.to_s.downcase).first | |
|
445 | having_mail(mail).first | |
|
425 | 446 | end |
|
426 | 447 | |
|
427 | 448 | # Returns true if the default admin account can no longer be used |
@@ -669,7 +690,7 class User < Principal | |||
|
669 | 690 | def self.anonymous |
|
670 | 691 | anonymous_user = AnonymousUser.first |
|
671 | 692 | if anonymous_user.nil? |
|
672 |
anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', |
|
|
693 | anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :login => '', :status => 0) | |
|
673 | 694 | raise 'Unable to create the anonymous user.' if anonymous_user.new_record? |
|
674 | 695 | end |
|
675 | 696 | anonymous_user |
@@ -699,6 +720,10 class User < Principal | |||
|
699 | 720 | end |
|
700 | 721 | end |
|
701 | 722 | |
|
723 | def instantiate_email_address | |
|
724 | email_address || build_email_address | |
|
725 | end | |
|
726 | ||
|
702 | 727 | private |
|
703 | 728 | |
|
704 | 729 | def generate_password_if_needed |
@@ -708,16 +733,13 class User < Principal | |||
|
708 | 733 | end |
|
709 | 734 | end |
|
710 | 735 | |
|
711 |
# Delete all outstanding password reset tokens on password |
|
|
736 | # Delete all outstanding password reset tokens on password change. | |
|
712 | 737 | # Delete the autologin tokens on password change to prohibit session leakage. |
|
713 | 738 | # This helps to keep the account secure in case the associated email account |
|
714 | 739 | # was compromised. |
|
715 | 740 | def destroy_tokens |
|
716 | tokens = [] | |
|
717 |
tokens |
|
|
718 | tokens |= ['recovery'] if mail_changed? | |
|
719 | ||
|
720 | if tokens.any? | |
|
741 | if hashed_password_changed? | |
|
742 | tokens = ['recovery', 'autologin'] | |
|
721 | 743 | Token.where(:user_id => id, :action => tokens).delete_all |
|
722 | 744 | end |
|
723 | 745 | end |
@@ -779,6 +801,7 class AnonymousUser < User | |||
|
779 | 801 | def logged?; false end |
|
780 | 802 | def admin; false end |
|
781 | 803 | def name(*args); I18n.t(:label_user_anonymous) end |
|
804 | def mail=(*args); nil end | |
|
782 | 805 | def mail; nil end |
|
783 | 806 | def time_zone; nil end |
|
784 | 807 | def rss_key; nil end |
@@ -804,4 +827,9 class AnonymousUser < User | |||
|
804 | 827 | def destroy |
|
805 | 828 | false |
|
806 | 829 | end |
|
830 | ||
|
831 | protected | |
|
832 | ||
|
833 | def instantiate_email_address | |
|
834 | end | |
|
807 | 835 | end |
@@ -41,11 +41,13 class WikiContent < ActiveRecord::Base | |||
|
41 | 41 | page.nil? ? [] : page.attachments |
|
42 | 42 | end |
|
43 | 43 | |
|
44 | def notified_users | |
|
45 | project.notified_users.reject {|user| !visible?(user)} | |
|
46 | end | |
|
47 | ||
|
44 | 48 | # Returns the mail addresses of users that should be notified |
|
45 | 49 | def recipients |
|
46 | notified = project.notified_users | |
|
47 | notified.reject! {|user| !visible?(user)} | |
|
48 | notified.collect(&:mail) | |
|
50 | notified_users.collect(&:mail) | |
|
49 | 51 | end |
|
50 | 52 | |
|
51 | 53 | # Return true if the content is the current page content |
@@ -1,4 +1,5 | |||
|
1 | 1 | <div class="contextual"> |
|
2 | <%= additional_emails_link(@user) %> | |
|
2 | 3 | <%= link_to(l(:button_change_password), {:action => 'password'}, :class => 'icon icon-passwd') if @user.change_password_allowed? %> |
|
3 | 4 | <%= call_hook(:view_my_account_contextual, :user => @user)%> |
|
4 | 5 | </div> |
@@ -16,6 +16,8 | |||
|
16 | 16 | |
|
17 | 17 | <p><%= setting_check_box :lost_password, :label => :label_password_lost %></p> |
|
18 | 18 | |
|
19 | <p><%= setting_text_field :max_additional_emails, :size => 6 %></p> | |
|
20 | ||
|
19 | 21 | <p><%= setting_check_box :openid, :disabled => !Object.const_defined?(:OpenID) %></p> |
|
20 | 22 | |
|
21 | 23 | <p><%= setting_check_box :rest_api_enabled %></p> |
@@ -1,5 +1,6 | |||
|
1 | 1 | <div class="contextual"> |
|
2 | 2 | <%= link_to l(:label_profile), user_path(@user), :class => 'icon icon-user' %> |
|
3 | <%= additional_emails_link(@user) %> | |
|
3 | 4 | <%= change_status_link(@user) %> |
|
4 | 5 | <%= delete_link user_path(@user) if User.current != @user %> |
|
5 | 6 | </div> |
@@ -5,8 +5,7 module ActiveRecord | |||
|
5 | 5 | include Redmine::I18n |
|
6 | 6 | # Translate attribute names for validation errors display |
|
7 | 7 | def self.human_attribute_name(attr, *args) |
|
8 | attr = attr.to_s.sub(/_id$/, '') | |
|
9 | ||
|
8 | attr = attr.to_s.sub(/_id$/, '').sub(/^.+\./, '') | |
|
10 | 9 | l("field_#{name.underscore.gsub('/', '_')}_#{attr}", :default => ["field_#{attr}".to_sym, attr]) |
|
11 | 10 | end |
|
12 | 11 | end |
@@ -227,6 +227,7 en: | |||
|
227 | 227 | field_firstname: First name |
|
228 | 228 | field_lastname: Last name |
|
229 | 229 | field_mail: Email |
|
230 | field_address: Email | |
|
230 | 231 | field_filename: File |
|
231 | 232 | field_filesize: Size |
|
232 | 233 | field_downloads: Downloads |
@@ -413,6 +414,7 en: | |||
|
413 | 414 | setting_force_default_language_for_anonymous: Force default language for anonymous users |
|
414 | 415 | setting_force_default_language_for_loggedin: Force default language for logged-in users |
|
415 | 416 | setting_link_copied_issue: Link issues on copy |
|
417 | setting_max_additional_emails: Maximum number of additional email addresses | |
|
416 | 418 | |
|
417 | 419 | permission_add_project: Create project |
|
418 | 420 | permission_add_subprojects: Create subprojects |
@@ -931,6 +933,10 en: | |||
|
931 | 933 | label_search_attachments_no: Do not search attachments |
|
932 | 934 | label_search_attachments_only: Search attachments only |
|
933 | 935 | label_search_open_issues_only: Open issues only |
|
936 | label_email_address_plural: Emails | |
|
937 | label_email_address_add: Add email address | |
|
938 | label_enable_notifications: Enable notifications | |
|
939 | label_disable_notifications: Disable notifications | |
|
934 | 940 | |
|
935 | 941 | button_login: Login |
|
936 | 942 | button_submit: Submit |
@@ -247,6 +247,7 fr: | |||
|
247 | 247 | field_firstname: PrΓ©nom |
|
248 | 248 | field_lastname: Nom |
|
249 | 249 | field_mail: Email |
|
250 | field_address: Email | |
|
250 | 251 | field_filename: Fichier |
|
251 | 252 | field_filesize: Taille |
|
252 | 253 | field_downloads: TΓ©lΓ©chargements |
@@ -433,6 +434,7 fr: | |||
|
433 | 434 | setting_force_default_language_for_anonymous: Forcer la langue par dΓ©fault pour les utilisateurs anonymes |
|
434 | 435 | setting_force_default_language_for_loggedin: Forcer la langue par dΓ©fault pour les utilisateurs identifiΓ©s |
|
435 | 436 | setting_link_copied_issue: Lier les demandes lors de la copie |
|
437 | setting_max_additional_emails: Nombre maximal d'adresses email additionnelles | |
|
436 | 438 | |
|
437 | 439 | permission_add_project: CrΓ©er un projet |
|
438 | 440 | permission_add_subprojects: CrΓ©er des sous-projets |
@@ -951,6 +953,10 fr: | |||
|
951 | 953 | label_search_attachments_no: Ne pas rechercher les fichiers |
|
952 | 954 | label_search_attachments_only: Rechercher les fichiers uniquement |
|
953 | 955 | label_search_open_issues_only: Demandes ouvertes uniquement |
|
956 | label_email_address_plural: Emails | |
|
957 | label_email_address_add: Ajouter une adresse email | |
|
958 | label_enable_notifications: Activer les notifications | |
|
959 | label_disable_notifications: DΓ©sactiver les notifications | |
|
954 | 960 | |
|
955 | 961 | button_login: Connexion |
|
956 | 962 | button_submit: Soumettre |
@@ -75,6 +75,7 Rails.application.routes.draw do | |||
|
75 | 75 | |
|
76 | 76 | resources :users do |
|
77 | 77 | resources :memberships, :controller => 'principal_memberships' |
|
78 | resources :email_addresses, :only => [:index, :create, :update, :destroy] | |
|
78 | 79 | end |
|
79 | 80 | |
|
80 | 81 | post 'watchers/watch', :to => 'watchers#watch', :as => 'watch' |
@@ -36,6 +36,10 unsubscribe: | |||
|
36 | 36 | password_min_length: |
|
37 | 37 | format: int |
|
38 | 38 | default: 8 |
|
39 | # Maximum number of additional email addresses per user | |
|
40 | max_additional_emails: | |
|
41 | format: int | |
|
42 | default: 5 | |
|
39 | 43 | # Maximum lifetime of user sessions in minutes |
|
40 | 44 | session_lifetime: |
|
41 | 45 | format: int |
@@ -378,10 +378,10 function setPredecessorFieldsVisibility() { | |||
|
378 | 378 | } |
|
379 | 379 | } |
|
380 | 380 | |
|
381 | function showModal(id, width) { | |
|
381 | function showModal(id, width, title) { | |
|
382 | 382 | var el = $('#'+id).first(); |
|
383 | 383 | if (el.length === 0 || el.is(':visible')) {return;} |
|
384 |
|
|
|
384 | if (!title) title = el.find('h3.title').text(); | |
|
385 | 385 | el.dialog({ |
|
386 | 386 | width: width, |
|
387 | 387 | modal: true, |
@@ -132,6 +132,7 table.list td.checkbox { width: 15px; padding: 2px 0 0 0; } | |||
|
132 | 132 | table.list td.checkbox input {padding:0px;} |
|
133 | 133 | table.list td.buttons { width: 15%; white-space:nowrap; text-align: right; } |
|
134 | 134 | table.list td.buttons a { padding-right: 0.6em; } |
|
135 | table.list td.buttons img {vertical-align:middle;} | |
|
135 | 136 | table.list td.reorder {width:15%; white-space:nowrap; text-align:center; } |
|
136 | 137 | table.list caption { text-align: left; padding: 0.5em 0.5em 0.5em 0; } |
|
137 | 138 | |
@@ -209,7 +210,7 tr.version.shared td.name { background: url(../images/link.png) no-repeat 0% 70% | |||
|
209 | 210 | tr.version td.date, tr.version td.status, tr.version td.sharing { text-align: center; white-space:nowrap; } |
|
210 | 211 | |
|
211 | 212 | tr.user td {width:13%;white-space: nowrap;} |
|
212 |
|
|
|
213 | td.username, td.firstname, td.lastname, td.email {text-align:left !important;} | |
|
213 | 214 | tr.user td.email { width:18%; } |
|
214 | 215 | tr.user.locked, tr.user.registered { color: #aaa; } |
|
215 | 216 | tr.user.locked a, tr.user.registered a { color: #aaa; } |
@@ -1046,6 +1047,7 a.close-icon:hover {background-image:url('../images/close_hl.png');} | |||
|
1046 | 1047 | .icon-zoom-out { background-image: url(../images/zoom_out.png); } |
|
1047 | 1048 | .icon-passwd { background-image: url(../images/textfield_key.png); } |
|
1048 | 1049 | .icon-test { background-image: url(../images/bullet_go.png); } |
|
1050 | .icon-email-add { background-image: url(../images/email_add.png); } | |
|
1049 | 1051 | |
|
1050 | 1052 | .icon-file { background-image: url(../images/files/default.png); } |
|
1051 | 1053 | .icon-file.text-plain { background-image: url(../images/files/text.png); } |
@@ -1,22 +1,4 | |||
|
1 | 1 | --- |
|
2 | users_004: | |
|
3 | created_on: 2006-07-19 19:34:07 +02:00 | |
|
4 | status: 1 | |
|
5 | last_login_on: | |
|
6 | language: en | |
|
7 | # password = foo | |
|
8 | salt: 3126f764c3c5ac61cbfc103f25f934cf | |
|
9 | hashed_password: 9e4dd7eeb172c12a0691a6d9d3a269f7e9fe671b | |
|
10 | updated_on: 2006-07-19 19:34:07 +02:00 | |
|
11 | admin: false | |
|
12 | mail: rhill@somenet.foo | |
|
13 | lastname: Hill | |
|
14 | firstname: Robert | |
|
15 | id: 4 | |
|
16 | auth_source_id: | |
|
17 | mail_notification: all | |
|
18 | login: rhill | |
|
19 | type: User | |
|
20 | 2 | users_001: |
|
21 | 3 | created_on: 2006-07-19 19:12:21 +02:00 |
|
22 | 4 | status: 1 |
@@ -27,7 +9,6 users_001: | |||
|
27 | 9 | hashed_password: b5b6ff9543bf1387374cdfa27a54c96d236a7150 |
|
28 | 10 | updated_on: 2006-07-19 22:57:52 +02:00 |
|
29 | 11 | admin: true |
|
30 | mail: admin@somenet.foo | |
|
31 | 12 | lastname: Admin |
|
32 | 13 | firstname: Redmine |
|
33 | 14 | id: 1 |
@@ -45,7 +26,6 users_002: | |||
|
45 | 26 | hashed_password: bfbe06043353a677d0215b26a5800d128d5413bc |
|
46 | 27 | updated_on: 2006-07-19 22:42:15 +02:00 |
|
47 | 28 | admin: false |
|
48 | mail: jsmith@somenet.foo | |
|
49 | 29 | lastname: Smith |
|
50 | 30 | firstname: John |
|
51 | 31 | id: 2 |
@@ -63,7 +43,6 users_003: | |||
|
63 | 43 | hashed_password: 8f659c8d7c072f189374edacfa90d6abbc26d8ed |
|
64 | 44 | updated_on: 2006-07-19 19:33:19 +02:00 |
|
65 | 45 | admin: false |
|
66 | mail: dlopper@somenet.foo | |
|
67 | 46 | lastname: Lopper |
|
68 | 47 | firstname: Dave |
|
69 | 48 | id: 3 |
@@ -71,6 +50,23 users_003: | |||
|
71 | 50 | mail_notification: all |
|
72 | 51 | login: dlopper |
|
73 | 52 | type: User |
|
53 | users_004: | |
|
54 | created_on: 2006-07-19 19:34:07 +02:00 | |
|
55 | status: 1 | |
|
56 | last_login_on: | |
|
57 | language: en | |
|
58 | # password = foo | |
|
59 | salt: 3126f764c3c5ac61cbfc103f25f934cf | |
|
60 | hashed_password: 9e4dd7eeb172c12a0691a6d9d3a269f7e9fe671b | |
|
61 | updated_on: 2006-07-19 19:34:07 +02:00 | |
|
62 | admin: false | |
|
63 | lastname: Hill | |
|
64 | firstname: Robert | |
|
65 | id: 4 | |
|
66 | auth_source_id: | |
|
67 | mail_notification: all | |
|
68 | login: rhill | |
|
69 | type: User | |
|
74 | 70 | users_005: |
|
75 | 71 | id: 5 |
|
76 | 72 | created_on: 2006-07-19 19:33:19 +02:00 |
@@ -81,7 +77,6 users_005: | |||
|
81 | 77 | hashed_password: 1 |
|
82 | 78 | updated_on: 2006-07-19 19:33:19 +02:00 |
|
83 | 79 | admin: false |
|
84 | mail: dlopper2@somenet.foo | |
|
85 | 80 | lastname: Lopper2 |
|
86 | 81 | firstname: Dave2 |
|
87 | 82 | auth_source_id: |
@@ -97,7 +92,6 users_006: | |||
|
97 | 92 | hashed_password: 1 |
|
98 | 93 | updated_on: 2006-07-19 19:33:19 +02:00 |
|
99 | 94 | admin: false |
|
100 | mail: '' | |
|
101 | 95 | lastname: Anonymous |
|
102 | 96 | firstname: '' |
|
103 | 97 | auth_source_id: |
@@ -116,7 +110,6 users_007: | |||
|
116 | 110 | hashed_password: 8f659c8d7c072f189374edacfa90d6abbc26d8ed |
|
117 | 111 | updated_on: 2006-07-19 19:33:19 +02:00 |
|
118 | 112 | admin: false |
|
119 | mail: someone@foo.bar | |
|
120 | 113 | lastname: One |
|
121 | 114 | firstname: Some |
|
122 | 115 | auth_source_id: |
@@ -134,7 +127,6 users_008: | |||
|
134 | 127 | hashed_password: 8f659c8d7c072f189374edacfa90d6abbc26d8ed |
|
135 | 128 | updated_on: 2006-07-19 19:33:19 +02:00 |
|
136 | 129 | admin: false |
|
137 | mail: miscuser8@foo.bar | |
|
138 | 130 | lastname: Misc |
|
139 | 131 | firstname: User |
|
140 | 132 | auth_source_id: |
@@ -150,7 +142,6 users_009: | |||
|
150 | 142 | hashed_password: 1 |
|
151 | 143 | updated_on: 2006-07-19 19:33:19 +02:00 |
|
152 | 144 | admin: false |
|
153 | mail: miscuser9@foo.bar | |
|
154 | 145 | lastname: Misc |
|
155 | 146 | firstname: User |
|
156 | 147 | auth_source_id: |
@@ -18,7 +18,7 | |||
|
18 | 18 | require File.expand_path('../../test_helper', __FILE__) |
|
19 | 19 | |
|
20 | 20 | class AdminControllerTest < ActionController::TestCase |
|
21 | fixtures :projects, :users, :roles | |
|
21 | fixtures :projects, :users, :email_addresses, :roles | |
|
22 | 22 | |
|
23 | 23 | def setup |
|
24 | 24 | User.current = nil |
@@ -18,7 +18,7 | |||
|
18 | 18 | require File.expand_path('../../test_helper', __FILE__) |
|
19 | 19 | |
|
20 | 20 | class DocumentsControllerTest < ActionController::TestCase |
|
21 | fixtures :projects, :users, :roles, :members, :member_roles, | |
|
21 | fixtures :projects, :users, :email_addresses, :roles, :members, :member_roles, | |
|
22 | 22 | :enabled_modules, :documents, :enumerations, |
|
23 | 23 | :groups_users, :attachments |
|
24 | 24 |
@@ -19,7 +19,7 require File.expand_path('../../test_helper', __FILE__) | |||
|
19 | 19 | |
|
20 | 20 | class IssuesControllerTest < ActionController::TestCase |
|
21 | 21 | fixtures :projects, |
|
22 | :users, | |
|
22 | :users, :email_addresses, | |
|
23 | 23 | :roles, |
|
24 | 24 | :members, |
|
25 | 25 | :member_roles, |
@@ -20,7 +20,7 require File.expand_path('../../test_helper', __FILE__) | |||
|
20 | 20 | class IssuesCustomFieldsVisibilityTest < ActionController::TestCase |
|
21 | 21 | tests IssuesController |
|
22 | 22 | fixtures :projects, |
|
23 | :users, | |
|
23 | :users, :email_addresses, | |
|
24 | 24 | :roles, |
|
25 | 25 | :members, |
|
26 | 26 | :member_roles, |
@@ -18,7 +18,7 | |||
|
18 | 18 | require File.expand_path('../../test_helper', __FILE__) |
|
19 | 19 | |
|
20 | 20 | class MailHandlerControllerTest < ActionController::TestCase |
|
21 | fixtures :users, :projects, :enabled_modules, :roles, :members, :member_roles, :issues, :issue_statuses, | |
|
21 | fixtures :users, :email_addresses, :projects, :enabled_modules, :roles, :members, :member_roles, :issues, :issue_statuses, | |
|
22 | 22 | :trackers, :projects_trackers, :enumerations |
|
23 | 23 | |
|
24 | 24 | FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler' |
@@ -18,7 +18,7 | |||
|
18 | 18 | require File.expand_path('../../test_helper', __FILE__) |
|
19 | 19 | |
|
20 | 20 | class MessagesControllerTest < ActionController::TestCase |
|
21 | fixtures :projects, :users, :members, :member_roles, :roles, :boards, :messages, :enabled_modules | |
|
21 | fixtures :projects, :users, :email_addresses, :members, :member_roles, :roles, :boards, :messages, :enabled_modules | |
|
22 | 22 | |
|
23 | 23 | def setup |
|
24 | 24 | User.current = nil |
@@ -18,7 +18,7 | |||
|
18 | 18 | require File.expand_path('../../test_helper', __FILE__) |
|
19 | 19 | |
|
20 | 20 | class MyControllerTest < ActionController::TestCase |
|
21 | fixtures :users, :user_preferences, :roles, :projects, :members, :member_roles, | |
|
21 | fixtures :users, :email_addresses, :user_preferences, :roles, :projects, :members, :member_roles, | |
|
22 | 22 | :issues, :issue_statuses, :trackers, :enumerations, :custom_fields, :auth_sources |
|
23 | 23 | |
|
24 | 24 | def setup |
@@ -18,7 +18,7 | |||
|
18 | 18 | require File.expand_path('../../test_helper', __FILE__) |
|
19 | 19 | |
|
20 | 20 | class NewsControllerTest < ActionController::TestCase |
|
21 | fixtures :projects, :users, :roles, :members, :member_roles, | |
|
21 | fixtures :projects, :users, :email_addresses, :roles, :members, :member_roles, | |
|
22 | 22 | :enabled_modules, :news, :comments, |
|
23 | 23 | :attachments |
|
24 | 24 |
@@ -18,7 +18,7 | |||
|
18 | 18 | require File.expand_path('../../test_helper', __FILE__) |
|
19 | 19 | |
|
20 | 20 | class ProjectsControllerTest < ActionController::TestCase |
|
21 | fixtures :projects, :versions, :users, :roles, :members, | |
|
21 | fixtures :projects, :versions, :users, :email_addresses, :roles, :members, | |
|
22 | 22 | :member_roles, :issues, :journals, :journal_details, |
|
23 | 23 | :trackers, :projects_trackers, :issue_statuses, |
|
24 | 24 | :enabled_modules, :enumerations, :boards, :messages, |
@@ -20,7 +20,7 require File.expand_path('../../test_helper', __FILE__) | |||
|
20 | 20 | class RepositoriesBazaarControllerTest < ActionController::TestCase |
|
21 | 21 | tests RepositoriesController |
|
22 | 22 | |
|
23 | fixtures :projects, :users, :roles, :members, :member_roles, | |
|
23 | fixtures :projects, :users, :email_addresses, :roles, :members, :member_roles, | |
|
24 | 24 | :repositories, :enabled_modules |
|
25 | 25 | |
|
26 | 26 | REPOSITORY_PATH = Rails.root.join('tmp/test/bazaar_repository').to_s |
@@ -18,7 +18,7 | |||
|
18 | 18 | require File.expand_path('../../test_helper', __FILE__) |
|
19 | 19 | |
|
20 | 20 | class RepositoriesControllerTest < ActionController::TestCase |
|
21 | fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, | |
|
21 | fixtures :projects, :users, :email_addresses, :roles, :members, :member_roles, :enabled_modules, | |
|
22 | 22 | :repositories, :issues, :issue_statuses, :changesets, :changes, |
|
23 | 23 | :issue_categories, :enumerations, :custom_fields, :custom_values, :trackers |
|
24 | 24 |
@@ -20,7 +20,7 require File.expand_path('../../test_helper', __FILE__) | |||
|
20 | 20 | class RepositoriesCvsControllerTest < ActionController::TestCase |
|
21 | 21 | tests RepositoriesController |
|
22 | 22 | |
|
23 | fixtures :projects, :users, :roles, :members, :member_roles, | |
|
23 | fixtures :projects, :users, :email_addresses, :roles, :members, :member_roles, | |
|
24 | 24 | :repositories, :enabled_modules |
|
25 | 25 | |
|
26 | 26 | REPOSITORY_PATH = Rails.root.join('tmp/test/cvs_repository').to_s |
@@ -20,7 +20,7 require File.expand_path('../../test_helper', __FILE__) | |||
|
20 | 20 | class RepositoriesDarcsControllerTest < ActionController::TestCase |
|
21 | 21 | tests RepositoriesController |
|
22 | 22 | |
|
23 | fixtures :projects, :users, :roles, :members, :member_roles, | |
|
23 | fixtures :projects, :users, :email_addresses, :roles, :members, :member_roles, | |
|
24 | 24 | :repositories, :enabled_modules |
|
25 | 25 | |
|
26 | 26 | REPOSITORY_PATH = Rails.root.join('tmp/test/darcs_repository').to_s |
@@ -20,7 +20,7 require File.expand_path('../../test_helper', __FILE__) | |||
|
20 | 20 | class RepositoriesFilesystemControllerTest < ActionController::TestCase |
|
21 | 21 | tests RepositoriesController |
|
22 | 22 | |
|
23 | fixtures :projects, :users, :roles, :members, :member_roles, | |
|
23 | fixtures :projects, :users, :email_addresses, :roles, :members, :member_roles, | |
|
24 | 24 | :repositories, :enabled_modules |
|
25 | 25 | |
|
26 | 26 | REPOSITORY_PATH = Rails.root.join('tmp/test/filesystem_repository').to_s |
@@ -20,7 +20,7 require File.expand_path('../../test_helper', __FILE__) | |||
|
20 | 20 | class RepositoriesGitControllerTest < ActionController::TestCase |
|
21 | 21 | tests RepositoriesController |
|
22 | 22 | |
|
23 | fixtures :projects, :users, :roles, :members, :member_roles, | |
|
23 | fixtures :projects, :users, :email_addresses, :roles, :members, :member_roles, | |
|
24 | 24 | :repositories, :enabled_modules |
|
25 | 25 | |
|
26 | 26 | REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s |
@@ -20,7 +20,7 require File.expand_path('../../test_helper', __FILE__) | |||
|
20 | 20 | class RepositoriesMercurialControllerTest < ActionController::TestCase |
|
21 | 21 | tests RepositoriesController |
|
22 | 22 | |
|
23 | fixtures :projects, :users, :roles, :members, :member_roles, | |
|
23 | fixtures :projects, :users, :email_addresses, :roles, :members, :member_roles, | |
|
24 | 24 | :repositories, :enabled_modules |
|
25 | 25 | |
|
26 | 26 | REPOSITORY_PATH = Rails.root.join('tmp/test/mercurial_repository').to_s |
@@ -20,7 +20,7 require File.expand_path('../../test_helper', __FILE__) | |||
|
20 | 20 | class RepositoriesSubversionControllerTest < ActionController::TestCase |
|
21 | 21 | tests RepositoriesController |
|
22 | 22 | |
|
23 | fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, | |
|
23 | fixtures :projects, :users, :email_addresses, :roles, :members, :member_roles, :enabled_modules, | |
|
24 | 24 | :repositories, :issues, :issue_statuses, :changesets, :changes, |
|
25 | 25 | :issue_categories, :enumerations, :custom_fields, :custom_values, :trackers |
|
26 | 26 |
@@ -20,7 +20,7 require File.expand_path('../../test_helper', __FILE__) | |||
|
20 | 20 | class UsersControllerTest < ActionController::TestCase |
|
21 | 21 | include Redmine::I18n |
|
22 | 22 | |
|
23 | fixtures :users, :projects, :members, :member_roles, :roles, | |
|
23 | fixtures :users, :email_addresses, :projects, :members, :member_roles, :roles, | |
|
24 | 24 | :custom_fields, :custom_values, :groups_users, |
|
25 | 25 | :auth_sources, |
|
26 | 26 | :enabled_modules, |
@@ -18,7 +18,7 | |||
|
18 | 18 | require File.expand_path('../../test_helper', __FILE__) |
|
19 | 19 | |
|
20 | 20 | class WikiControllerTest < ActionController::TestCase |
|
21 | fixtures :projects, :users, :roles, :members, :member_roles, | |
|
21 | fixtures :projects, :users, :email_addresses, :roles, :members, :member_roles, | |
|
22 | 22 | :enabled_modules, :wikis, :wiki_pages, :wiki_contents, |
|
23 | 23 | :wiki_content_versions, :attachments, |
|
24 | 24 | :issues, :issue_statuses |
@@ -18,7 +18,7 | |||
|
18 | 18 | require File.expand_path('../../test_helper', __FILE__) |
|
19 | 19 | |
|
20 | 20 | class AccountTest < Redmine::IntegrationTest |
|
21 | fixtures :users, :roles | |
|
21 | fixtures :users, :email_addresses, :roles | |
|
22 | 22 | |
|
23 | 23 | def test_login |
|
24 | 24 | get "/my/page" |
@@ -18,7 +18,7 | |||
|
18 | 18 | require File.expand_path('../../../test_helper', __FILE__) |
|
19 | 19 | |
|
20 | 20 | class Redmine::ApiTest::UsersTest < Redmine::ApiTest::Base |
|
21 | fixtures :users, :members, :member_roles, :roles, :projects | |
|
21 | fixtures :users, :email_addresses, :members, :member_roles, :roles, :projects | |
|
22 | 22 | |
|
23 | 23 | test "GET /users.xml should return users" do |
|
24 | 24 | get '/users.xml', {}, credentials('admin') |
@@ -19,7 +19,7 require File.expand_path('../../test_helper', __FILE__) | |||
|
19 | 19 | |
|
20 | 20 | class IssuesTest < Redmine::IntegrationTest |
|
21 | 21 | fixtures :projects, |
|
22 | :users, | |
|
22 | :users, :email_addresses, | |
|
23 | 23 | :roles, |
|
24 | 24 | :members, |
|
25 | 25 | :member_roles, |
@@ -18,7 +18,7 | |||
|
18 | 18 | require File.expand_path('../../test_helper', __FILE__) |
|
19 | 19 | |
|
20 | 20 | class UsersTest < Redmine::IntegrationTest |
|
21 | fixtures :users | |
|
21 | fixtures :users, :email_addresses | |
|
22 | 22 | |
|
23 | 23 | def test_destroy_should_not_accept_get_requests |
|
24 | 24 | assert_no_difference 'User.count' do |
@@ -20,7 +20,7 require File.expand_path('../../test_helper', __FILE__) | |||
|
20 | 20 | class DocumentTest < ActiveSupport::TestCase |
|
21 | 21 | fixtures :projects, :enumerations, :documents, :attachments, |
|
22 | 22 | :enabled_modules, |
|
23 | :users, :members, :member_roles, :roles, | |
|
23 | :users, :email_addresses, :members, :member_roles, :roles, | |
|
24 | 24 | :groups_users |
|
25 | 25 | |
|
26 | 26 | def test_create |
@@ -18,7 +18,7 | |||
|
18 | 18 | require File.expand_path('../../test_helper', __FILE__) |
|
19 | 19 | |
|
20 | 20 | class IssueTest < ActiveSupport::TestCase |
|
21 | fixtures :projects, :users, :members, :member_roles, :roles, | |
|
21 | fixtures :projects, :users, :email_addresses, :members, :member_roles, :roles, | |
|
22 | 22 | :groups_users, |
|
23 | 23 | :trackers, :projects_trackers, |
|
24 | 24 | :enabled_modules, |
@@ -223,6 +223,17 class MailHandlerTest < ActiveSupport::TestCase | |||
|
223 | 223 | assert_equal 1, issue.watcher_user_ids.size |
|
224 | 224 | end |
|
225 | 225 | |
|
226 | def test_add_issue_from_additional_email_address | |
|
227 | user = User.find(2) | |
|
228 | user.mail = 'mainaddress@somenet.foo' | |
|
229 | user.save! | |
|
230 | EmailAddress.create!(:user => user, :address => 'jsmith@somenet.foo') | |
|
231 | ||
|
232 | issue = submit_email('ticket_on_given_project.eml') | |
|
233 | assert issue | |
|
234 | assert_equal user, issue.author | |
|
235 | end | |
|
236 | ||
|
226 | 237 | def test_add_issue_by_unknown_user |
|
227 | 238 | assert_no_difference 'User.count' do |
|
228 | 239 | assert_equal false, |
@@ -20,7 +20,7 require File.expand_path('../../test_helper', __FILE__) | |||
|
20 | 20 | class MailerTest < ActiveSupport::TestCase |
|
21 | 21 | include Redmine::I18n |
|
22 | 22 | include ActionDispatch::Assertions::SelectorAssertions |
|
23 | fixtures :projects, :enabled_modules, :issues, :users, :members, | |
|
23 | fixtures :projects, :enabled_modules, :issues, :users, :email_addresses, :members, | |
|
24 | 24 | :member_roles, :roles, :documents, :attachments, :news, |
|
25 | 25 | :tokens, :journals, :journal_details, :changesets, |
|
26 | 26 | :trackers, :projects_trackers, |
@@ -298,6 +298,14 class MailerTest < ActiveSupport::TestCase | |||
|
298 | 298 | assert last_email.bcc.include?('dlopper@somenet.foo') |
|
299 | 299 | end |
|
300 | 300 | |
|
301 | def test_issue_add_should_send_mail_to_all_user_email_address | |
|
302 | EmailAddress.create!(:user_id => 3, :address => 'otheremail@somenet.foo') | |
|
303 | issue = Issue.find(1) | |
|
304 | assert Mailer.deliver_issue_add(issue) | |
|
305 | assert last_email.bcc.include?('dlopper@somenet.foo') | |
|
306 | assert last_email.bcc.include?('otheremail@somenet.foo') | |
|
307 | end | |
|
308 | ||
|
301 | 309 | test "#issue_add should not notify project members that are not allow to view the issue" do |
|
302 | 310 | issue = Issue.find(1) |
|
303 | 311 | Role.find(2).remove_permission!(:view_issues) |
@@ -771,6 +779,30 class MailerTest < ActiveSupport::TestCase | |||
|
771 | 779 | ActionMailer::Base.delivery_method = :test |
|
772 | 780 | end |
|
773 | 781 | |
|
782 | def test_email_addresses_should_keep_addresses | |
|
783 | assert_equal ["foo@example.net"], | |
|
784 | Mailer.email_addresses("foo@example.net") | |
|
785 | ||
|
786 | assert_equal ["foo@example.net", "bar@example.net"], | |
|
787 | Mailer.email_addresses(["foo@example.net", "bar@example.net"]) | |
|
788 | end | |
|
789 | ||
|
790 | def test_email_addresses_should_replace_users_with_their_email_addresses | |
|
791 | assert_equal ["admin@somenet.foo"], | |
|
792 | Mailer.email_addresses(User.find(1)) | |
|
793 | ||
|
794 | assert_equal ["admin@somenet.foo", "jsmith@somenet.foo"], | |
|
795 | Mailer.email_addresses(User.where(:id => [1,2])).sort | |
|
796 | end | |
|
797 | ||
|
798 | def test_email_addresses_should_include_notified_emails_addresses_only | |
|
799 | EmailAddress.create!(:user_id => 2, :address => "another@somenet.foo", :notify => false) | |
|
800 | EmailAddress.create!(:user_id => 2, :address => "another2@somenet.foo") | |
|
801 | ||
|
802 | assert_equal ["another2@somenet.foo", "jsmith@somenet.foo"], | |
|
803 | Mailer.email_addresses(User.find(2)).sort | |
|
804 | end | |
|
805 | ||
|
774 | 806 | private |
|
775 | 807 | |
|
776 | 808 | def last_email |
@@ -18,7 +18,7 | |||
|
18 | 18 | require File.expand_path('../../test_helper', __FILE__) |
|
19 | 19 | |
|
20 | 20 | class UserTest < ActiveSupport::TestCase |
|
21 | fixtures :users, :members, :projects, :roles, :member_roles, :auth_sources, | |
|
21 | fixtures :users, :email_addresses, :members, :projects, :roles, :member_roles, :auth_sources, | |
|
22 | 22 | :trackers, :issue_statuses, |
|
23 | 23 | :projects_trackers, |
|
24 | 24 | :watchers, |
@@ -57,11 +57,41 class UserTest < ActiveSupport::TestCase | |||
|
57 | 57 | assert_equal "foo@bar.com", u.mail |
|
58 | 58 | end |
|
59 | 59 | |
|
60 | def test_mail_validation | |
|
61 | u = User.new | |
|
60 | def test_should_create_email_address | |
|
61 | u = User.new(:firstname => "new", :lastname => "user") | |
|
62 | u.login = "create_email_address" | |
|
63 | u.mail = "defaultemail@somenet.foo" | |
|
64 | assert u.save | |
|
65 | u.reload | |
|
66 | assert u.email_address | |
|
67 | assert_equal "defaultemail@somenet.foo", u.email_address.address | |
|
68 | assert_equal true, u.email_address.is_default | |
|
69 | assert_equal true, u.email_address.notify | |
|
70 | end | |
|
71 | ||
|
72 | def test_should_not_create_user_without_mail | |
|
73 | set_language_if_valid 'en' | |
|
74 | u = User.new(:firstname => "new", :lastname => "user") | |
|
75 | u.login = "user_without_mail" | |
|
76 | assert !u.save | |
|
77 | assert_equal ["Email #{I18n.translate('activerecord.errors.messages.blank')}"], u.errors.full_messages | |
|
78 | end | |
|
79 | ||
|
80 | def test_should_not_create_user_with_blank_mail | |
|
81 | set_language_if_valid 'en' | |
|
82 | u = User.new(:firstname => "new", :lastname => "user") | |
|
83 | u.login = "user_with_blank_mail" | |
|
84 | u.mail = '' | |
|
85 | assert !u.save | |
|
86 | assert_equal ["Email #{I18n.translate('activerecord.errors.messages.blank')}"], u.errors.full_messages | |
|
87 | end | |
|
88 | ||
|
89 | def test_should_not_update_user_with_blank_mail | |
|
90 | set_language_if_valid 'en' | |
|
91 | u = User.find(2) | |
|
62 | 92 | u.mail = '' |
|
63 |
assert !u. |
|
|
64 |
assert_ |
|
|
93 | assert !u.save | |
|
94 | assert_equal ["Email #{I18n.translate('activerecord.errors.messages.blank')}"], u.errors.full_messages | |
|
65 | 95 | end |
|
66 | 96 | |
|
67 | 97 | def test_login_length_validation |
@@ -151,6 +181,7 class UserTest < ActiveSupport::TestCase | |||
|
151 | 181 | end |
|
152 | 182 | |
|
153 | 183 | def test_mail_uniqueness_should_not_be_case_sensitive |
|
184 | set_language_if_valid 'en' | |
|
154 | 185 | u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo") |
|
155 | 186 | u.login = 'newuser1' |
|
156 | 187 | u.password, u.password_confirmation = "password", "password" |
@@ -160,7 +191,7 class UserTest < ActiveSupport::TestCase | |||
|
160 | 191 | u.login = 'newuser2' |
|
161 | 192 | u.password, u.password_confirmation = "password", "password" |
|
162 | 193 | assert !u.save |
|
163 |
assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors |
|
|
194 | assert_include "Email #{I18n.translate('activerecord.errors.messages.taken')}", u.errors.full_messages | |
|
164 | 195 | end |
|
165 | 196 | |
|
166 | 197 | def test_update |
@@ -677,7 +708,7 class UserTest < ActiveSupport::TestCase | |||
|
677 | 708 | assert_kind_of AnonymousUser, anon1 |
|
678 | 709 | anon2 = AnonymousUser.create( |
|
679 | 710 | :lastname => 'Anonymous', :firstname => '', |
|
680 |
|
|
|
711 | :login => '', :status => 0) | |
|
681 | 712 | assert_equal 1, anon2.errors.count |
|
682 | 713 | end |
|
683 | 714 |
@@ -18,7 +18,7 | |||
|
18 | 18 | require File.expand_path('../../test_helper', __FILE__) |
|
19 | 19 | |
|
20 | 20 | class WatcherTest < ActiveSupport::TestCase |
|
21 | fixtures :projects, :users, :members, :member_roles, :roles, :enabled_modules, | |
|
21 | fixtures :projects, :users, :email_addresses, :members, :member_roles, :roles, :enabled_modules, | |
|
22 | 22 | :issues, :issue_statuses, :enumerations, :trackers, :projects_trackers, |
|
23 | 23 | :boards, :messages, |
|
24 | 24 | :wikis, :wiki_pages, |
General Comments 0
You need to be logged in to leave comments.
Login now