Index: app/helpers/projects_helper.rb
===================================================================
--- app/helpers/projects_helper.rb	(revision 4802)
+++ app/helpers/projects_helper.rb	(working copy)
@@ -30,7 +30,8 @@
             {:name => 'wiki', :action => :manage_wiki, :partial => 'projects/settings/wiki', :label => :label_wiki},
             {:name => 'repository', :action => :manage_repository, :partial => 'projects/settings/repository', :label => :label_repository},
             {:name => 'boards', :action => :manage_boards, :partial => 'projects/settings/boards', :label => :label_board_plural},
-            {:name => 'activities', :action => :manage_project_activities, :partial => 'projects/settings/activities', :label => :enumeration_activities}
+            {:name => 'activities', :action => :manage_project_activities, :partial => 'projects/settings/activities', :label => :enumeration_activities},
+            {:name => 'notifications', :action => :manage_project_notifications, :partial => 'projects/settings/notifications', :label => :field_mail_notification}
             ]
     tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}     
   end
@@ -105,4 +106,36 @@
     sharing = 'none' unless Version::VERSION_SHARINGS.include?(sharing)
     l("label_version_sharing_#{sharing}")
   end
+  
+  def setting_label(setting, options={})
+    label = options.delete(:label)
+    label != false ? content_tag("label", l(label || "setting_#{setting}")) : ''
+  end
+  
+  def setting_text_field(setting, options={})
+    setting_label(setting, options) +
+      text_field_tag("settings[#{setting}]", get_setting_value(setting), options)
+  end
+  
+  def setting_text_area(setting, options={})
+    setting_label(setting, options) +
+      text_area_tag("settings[#{setting}]", get_setting_value(setting), options)
+  end
+  
+  def setting_check_box(setting, options={})
+    setting_label(setting, options) +
+      hidden_field_tag("settings[#{setting}]", 0) +
+      check_box_tag("settings[#{setting}]", 1, get_setting_value(setting) == '1', options)
+  end
+  
+  def get_setting_value(setting)
+    setting = setting.to_s
+    if not @settings
+      @settings = Hash.new
+      @project.project_settings.each do |s|
+        @settings[s.name] = s
+      end
+    end
+    @settings[setting] ? @settings[setting].value : ''
+  end
 end
Index: app/models/mailer.rb
===================================================================
--- app/models/mailer.rb	(revision 4802)
+++ app/models/mailer.rb	(working copy)
@@ -48,7 +48,7 @@
     subject "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
     body :issue => issue,
          :issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
-    render_multipart('issue_add', body)
+    render_multipart('issue_add', body, issue.project)
   end
 
   # Builds a tmail object used to email recipients of the edited issue.
@@ -76,7 +76,7 @@
          :journal => journal,
          :issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
 
-    render_multipart('issue_edit', body)
+    render_multipart('issue_edit', body, issue.project)
   end
 
   def reminder(user, issues, days)
@@ -100,7 +100,7 @@
     subject "[#{document.project.name}] #{l(:label_document_new)}: #{document.title}"
     body :document => document,
          :document_url => url_for(:controller => 'documents', :action => 'show', :id => document)
-    render_multipart('document_added', body)
+    render_multipart('document_added', body, document.project)
   end
 
   # Builds a tmail object used to email recipients of a project when an attachements are added.
@@ -131,7 +131,7 @@
     body :attachments => attachments,
          :added_to => added_to,
          :added_to_url => added_to_url
-    render_multipart('attachments_added', body)
+    render_multipart('attachments_added', body, container.project)
   end
   
   # Builds a tmail object used to email recipients of a news' project when a news item is added.
@@ -146,7 +146,7 @@
     subject "[#{news.project.name}] #{l(:label_news)}: #{news.title}"
     body :news => news,
          :news_url => url_for(:controller => 'news', :action => 'show', :id => news)
-    render_multipart('news_added', body)
+    render_multipart('news_added', body, news.project)
   end
 
   # Builds a tmail object used to email the recipients of the specified message that was posted. 
@@ -164,7 +164,7 @@
     subject "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}"
     body :message => message,
          :message_url => url_for(message.event_url)
-    render_multipart('message_posted', body)
+    render_multipart('message_posted', body, message.board.project)
   end
   
   # Builds a tmail object used to email the recipients of a project of the specified wiki content was added. 
@@ -181,7 +181,7 @@
     subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_added, :id => wiki_content.page.pretty_title)}"
     body :wiki_content => wiki_content,
          :wiki_content_url => url_for(:controller => 'wiki', :action => 'show', :project_id => wiki_content.project, :id => wiki_content.page.title)
-    render_multipart('wiki_content_added', body)
+    render_multipart('wiki_content_added', body, wiki_content.project)
   end
   
   # Builds a tmail object used to email the recipients of a project of the specified wiki content was updated. 
@@ -199,7 +199,7 @@
     body :wiki_content => wiki_content,
          :wiki_content_url => url_for(:controller => 'wiki', :action => 'show', :project_id => wiki_content.project, :id => wiki_content.page.title),
          :wiki_diff_url => url_for(:controller => 'wiki', :action => 'diff', :project_id => wiki_content.project, :id => wiki_content.page.title, :version => wiki_content.version)
-    render_multipart('wiki_content_updated', body)
+    render_multipart('wiki_content_updated', body, wiki_content.project)
   end
 
   # Builds a tmail object used to email the specified user their account information.
@@ -367,17 +367,7 @@
     if @author.pref[:no_self_notified]
       recipients.delete(@author.mail) if recipients
       cc.delete(@author.mail) if cc
-    end
-    
-    notified_users = [recipients, cc].flatten.compact.uniq
-    # Rails would log recipients only, not cc and bcc
-    mylogger.info "Sending email notification to: #{notified_users.join(', ')}" if mylogger
-    
-    # Blind carbon copy recipients
-    if Setting.bcc_recipients?
-      bcc(notified_users)
-      recipients []
-      cc []
+      bcc.delete(@author.mail) if bcc
     end
     super
   end
@@ -389,8 +379,38 @@
   # https://rails.lighthouseapp.com/projects/8994/tickets/2338-actionmailer-mailer-views-and-content-type
   # https://rails.lighthouseapp.com/projects/8994/tickets/1799-actionmailer-doesnt-set-template_format-when-rendering-layouts
   
-  def render_multipart(method_name, body)
-    if Setting.plain_text_mail?
+  def render_multipart(method_name, body, project = nil)
+    # Check whether project settings override global ones
+    settings_hash = Hash.new
+    if not project.nil?
+      project.project_settings.each do |setting|
+        settings_hash[setting.name] = setting
+      end
+    end
+    from settings_hash['mail_from'].value if settings_hash['mail_from']
+    notified_users = [recipients, cc].flatten.compact.uniq
+    # Rails would log recipients only, not cc and bcc
+    mylogger.info "Sending email notification to: #{notified_users.join(', ')}" if mylogger
+    
+    if (settings_hash.has_key?('bcc_recipients') and settings_hash['bcc_recipients'].value == '1') or Setting.bcc_recipients?
+      bcc(notified_users)
+      recipients []
+      cc []
+    end
+    
+    if settings_hash.has_key?('emails_header')
+      body['emails_header'] = settings_hash['emails_header'].value
+    else
+      body['emails_header'] = Setting.emails_header
+    end
+    
+    if settings_hash.has_key?('emails_footer')
+      body['emails_footer'] = settings_hash['emails_footer'].value
+    else
+      body['emails_footer'] = Setting.emails_footer
+    end
+
+    if (settings_hash.has_key?('plain_text_mail') and settings_hash['plain_text_mail'].value == '1') or Setting.plain_text_mail?
       content_type "text/plain"
       body render(:file => "#{method_name}.text.plain.rhtml", :body => body, :layout => 'mailer.text.plain.erb')
     else
Index: app/models/project_setting.rb
===================================================================
--- app/models/project_setting.rb	(revision 0)
+++ app/models/project_setting.rb	(revision 0)
@@ -0,0 +1,12 @@
+class ProjectSetting < ActiveRecord::Base
+  belongs_to :project
+  # Possible setting names
+  NAMES = [:mail_from, :bcc_recipients, :plain_text_mail, :emails_header, :emails_footer]
+
+  def self.copy_settings(project)
+    settings = Setting.all(:conditions => { :name => self::NAMES })
+    settings.each do |setting|
+      self.new({ :name => setting.name, :value => setting.value, :project => project }).save
+    end
+  end
+end
\ No newline at end of file
Index: app/models/project.rb
===================================================================
--- app/models/project.rb	(revision 4802)
+++ app/models/project.rb	(working copy)
@@ -48,6 +48,7 @@
   has_many :boards, :dependent => :destroy, :order => "position ASC"
   has_one :repository, :dependent => :destroy
   has_many :changesets, :through => :repository
+  has_many :project_settings
   has_one :wiki, :dependent => :destroy
   # Custom field for the project issues
   has_and_belongs_to_many :issue_custom_fields, 
Index: app/controllers/projects_controller.rb
===================================================================
--- app/controllers/projects_controller.rb	(revision 4802)
+++ app/controllers/projects_controller.rb	(working copy)
@@ -83,6 +83,8 @@
         m = Member.new(:user => User.current, :roles => [r])
         @project.members << m
       end
+      # Copy email settings from global ones
+      ProjectSetting.copy_settings @project
       respond_to do |format|
         format.html { 
           flash[:notice] = l(:notice_successful_create)
@@ -120,6 +122,8 @@
         @project.enabled_module_names = params[:enabled_modules]
         if validate_parent_id && @project.copy(@source_project, :only => params[:only])
           @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
+          # Copy email settings from global ones
+          ProjectSetting.copy_settings @project
           flash[:notice] = l(:notice_successful_create)
           redirect_to :controller => 'projects', :action => 'settings', :id => @project
         elsif !@project.new_record?
@@ -176,6 +180,7 @@
     @trackers = Tracker.all
     @repository ||= @project.repository
     @wiki ||= @project.wiki
+    @deliveries = ActionMailer::Base.perform_deliveries
   end
   
   def edit
@@ -243,6 +248,24 @@
     # hide project in layout
     @project = nil
   end
+  
+  verify :method => :post, :only => :notifications, :render => {:nothing => true, :status => :method_not_allowed }
+  def notifications
+    if request.post? && params[:settings] && params[:settings].is_a?(Hash)
+      settings_hash = Hash.new
+      @project.project_settings.each do |setting|
+        settings_hash[setting.name] = setting
+      end
+      params[:settings].each do |name, value|
+        if settings_hash.has_key?(name)
+          settings_hash[name].value = value
+          settings_hash[name].save
+        end
+      end
+      flash[:notice] = l(:notice_successful_update)
+      redirect_to :action => 'settings', :id => @project, :tab => 'notifications'
+    end
+  end
 
 private
   def find_optional_project
Index: app/views/layouts/mailer.text.plain.erb
===================================================================
--- app/views/layouts/mailer.text.plain.erb	(revision 4802)
+++ app/views/layouts/mailer.text.plain.erb	(working copy)
@@ -1,4 +1,4 @@
-<%= Setting.emails_header %>
+<%= @emails_header %>
 <%= yield %>
 -- 
-<%= Setting.emails_footer %>
+<%= @emails_footer %>
Index: app/views/layouts/mailer.text.html.erb
===================================================================
--- app/views/layouts/mailer.text.html.erb	(revision 4802)
+++ app/views/layouts/mailer.text.html.erb	(working copy)
@@ -25,9 +25,9 @@
 </style>
 </head>
 <body>
-<span class="header"><%= Redmine::WikiFormatting.to_html(Setting.text_formatting, Setting.emails_header) %></span>
+<span class="header"><%= Redmine::WikiFormatting.to_html(Setting.text_formatting, @emails_header) %></span>
 <%= yield %>
 <hr />
-<span class="footer"><%= Redmine::WikiFormatting.to_html(Setting.text_formatting, Setting.emails_footer) %></span>
+<span class="footer"><%= Redmine::WikiFormatting.to_html(Setting.text_formatting, @emails_footer) %></span>
 </body>
 </html>
Index: app/views/projects/settings/_notifications.erb
===================================================================
--- app/views/projects/settings/_notifications.erb	(revision 0)
+++ app/views/projects/settings/_notifications.erb	(revision 0)
@@ -0,0 +1,29 @@
+<% if @deliveries %>
+<% form_for :project, @project,
+            :url => { :action => 'notifications', :id => @project },
+            :html => {:id => 'notifications-form'} do |f| %>
+
+<div class="box tabular settings">
+<p><%= setting_text_field :mail_from, :size => 60 %></p>
+
+<p><%= setting_check_box :bcc_recipients %></p>
+
+<p><%= setting_check_box :plain_text_mail %></p>
+
+</div>
+
+<fieldset class="box"><legend><%= l(:setting_emails_header) %></legend>
+<%= setting_text_area :emails_header, :label => false, :class => 'wiki-edit', :rows => 5 %>
+</fieldset>
+
+<fieldset class="box"><legend><%= l(:setting_emails_footer) %></legend>
+<%= setting_text_area :emails_footer, :label => false, :class => 'wiki-edit', :rows => 5 %>
+</fieldset>
+
+<%= submit_tag l(:button_save) %>
+<% end %>
+<% else %>
+<div class="nodata">
+<%= simple_format(l(:text_email_delivery_not_configured)) %>
+</div>
+<% end %>
Index: db/migrate/20110201111915_copy_email_settings.rb
===================================================================
--- db/migrate/20110201111915_copy_email_settings.rb	(revision 0)
+++ db/migrate/20110201111915_copy_email_settings.rb	(revision 0)
@@ -0,0 +1,13 @@
+class CopyEmailSettings < ActiveRecord::Migration
+  # Copy email settings from global ones into ProjectSetting
+  def self.up
+    settings = Setting.all(:conditions => { :name => ProjectSetting::NAMES })
+    Project.all.each do |project|
+      ProjectSetting.copy_settings project
+    end
+  end
+
+  def self.down
+    ProjectSetting.delete_all
+  end
+end
Index: db/migrate/20110201104137_create_project_settings.rb
===================================================================
--- db/migrate/20110201104137_create_project_settings.rb	(revision 0)
+++ db/migrate/20110201104137_create_project_settings.rb	(revision 0)
@@ -0,0 +1,15 @@
+class CreateProjectSettings < ActiveRecord::Migration
+  def self.up
+    create_table :project_settings do |t|
+      t.string :name
+      t.string :value
+      t.references :project
+
+      t.timestamps
+    end
+  end
+
+  def self.down
+    drop_table :project_settings
+  end
+end
Index: config/locales/en.yml
===================================================================
--- config/locales/en.yml	(revision 4802)
+++ config/locales/en.yml	(working copy)
@@ -369,6 +369,7 @@
   permission_select_project_modules: Select project modules
   permission_manage_members: Manage members
   permission_manage_project_activities: Manage project activities
+  permission_manage_project_notifications: Manage project notification settings
   permission_manage_versions: Manage versions
   permission_manage_categories: Manage issue categories
   permission_view_issues: View Issues
Index: config/routes.rb
===================================================================
--- config/routes.rb	(revision 4802)
+++ config/routes.rb	(working copy)
@@ -138,7 +138,8 @@
     :settings => :get,
     :modules => :post,
     :archive => :post,
-    :unarchive => :post
+    :unarchive => :post,
+    :notifications => :post
   } do |project|
     project.resource :project_enumerations, :as => 'enumerations', :only => [:update, :destroy]
     project.resources :files, :only => [:index, :new, :create]
Index: lib/redmine.rb
===================================================================
--- lib/redmine.rb	(revision 4802)
+++ lib/redmine.rb	(working copy)
@@ -49,6 +49,7 @@
   map.permission :search_project, {:search => :index}, :public => true
   map.permission :add_project, {:projects => [:new, :create]}, :require => :loggedin
   map.permission :edit_project, {:projects => [:settings, :edit, :update]}, :require => :member
+  map.permission :manage_project_notifications, {:projects => :notifications}, :require => :member
   map.permission :select_project_modules, {:projects => :modules}, :require => :member
   map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy, :autocomplete_for_member]}, :require => :member
   map.permission :manage_versions, {:projects => :settings, :versions => [:new, :create, :edit, :update, :close_completed, :destroy]}, :require => :member
