Project

General

Profile

Feature #7349 » project_notifications.diff

Denis Malinovsky, 2011-02-09 05:48

View differences:

app/helpers/projects_helper.rb (working copy)
30 30
            {:name => 'wiki', :action => :manage_wiki, :partial => 'projects/settings/wiki', :label => :label_wiki},
31 31
            {:name => 'repository', :action => :manage_repository, :partial => 'projects/settings/repository', :label => :label_repository},
32 32
            {:name => 'boards', :action => :manage_boards, :partial => 'projects/settings/boards', :label => :label_board_plural},
33
            {:name => 'activities', :action => :manage_project_activities, :partial => 'projects/settings/activities', :label => :enumeration_activities}
33
            {:name => 'activities', :action => :manage_project_activities, :partial => 'projects/settings/activities', :label => :enumeration_activities},
34
            {:name => 'notifications', :action => :manage_project_notifications, :partial => 'projects/settings/notifications', :label => :field_mail_notification}
34 35
            ]
35 36
    tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}     
36 37
  end
......
105 106
    sharing = 'none' unless Version::VERSION_SHARINGS.include?(sharing)
106 107
    l("label_version_sharing_#{sharing}")
107 108
  end
109
  
110
  def setting_label(setting, options={})
111
    label = options.delete(:label)
112
    label != false ? content_tag("label", l(label || "setting_#{setting}")) : ''
113
  end
114
  
115
  def setting_text_field(setting, options={})
116
    setting_label(setting, options) +
117
      text_field_tag("settings[#{setting}]", get_setting_value(setting), options)
118
  end
119
  
120
  def setting_text_area(setting, options={})
121
    setting_label(setting, options) +
122
      text_area_tag("settings[#{setting}]", get_setting_value(setting), options)
123
  end
124
  
125
  def setting_check_box(setting, options={})
126
    setting_label(setting, options) +
127
      hidden_field_tag("settings[#{setting}]", 0) +
128
      check_box_tag("settings[#{setting}]", 1, get_setting_value(setting) == '1', options)
129
  end
130
  
131
  def get_setting_value(setting)
132
    setting = setting.to_s
133
    if not @settings
134
      @settings = Hash.new
135
      @project.project_settings.each do |s|
136
        @settings[s.name] = s
137
      end
138
    end
139
    @settings[setting] ? @settings[setting].value : ''
140
  end
108 141
end
app/models/mailer.rb (working copy)
48 48
    subject "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
49 49
    body :issue => issue,
50 50
         :issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
51
    render_multipart('issue_add', body)
51
    render_multipart('issue_add', body, issue.project)
52 52
  end
53 53

  
54 54
  # Builds a tmail object used to email recipients of the edited issue.
......
76 76
         :journal => journal,
77 77
         :issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
78 78

  
79
    render_multipart('issue_edit', body)
79
    render_multipart('issue_edit', body, issue.project)
80 80
  end
81 81

  
82 82
  def reminder(user, issues, days)
......
100 100
    subject "[#{document.project.name}] #{l(:label_document_new)}: #{document.title}"
101 101
    body :document => document,
102 102
         :document_url => url_for(:controller => 'documents', :action => 'show', :id => document)
103
    render_multipart('document_added', body)
103
    render_multipart('document_added', body, document.project)
104 104
  end
105 105

  
106 106
  # Builds a tmail object used to email recipients of a project when an attachements are added.
......
131 131
    body :attachments => attachments,
132 132
         :added_to => added_to,
133 133
         :added_to_url => added_to_url
134
    render_multipart('attachments_added', body)
134
    render_multipart('attachments_added', body, container.project)
135 135
  end
136 136
  
137 137
  # Builds a tmail object used to email recipients of a news' project when a news item is added.
......
146 146
    subject "[#{news.project.name}] #{l(:label_news)}: #{news.title}"
147 147
    body :news => news,
148 148
         :news_url => url_for(:controller => 'news', :action => 'show', :id => news)
149
    render_multipart('news_added', body)
149
    render_multipart('news_added', body, news.project)
150 150
  end
151 151

  
152 152
  # Builds a tmail object used to email the recipients of the specified message that was posted. 
......
164 164
    subject "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}"
165 165
    body :message => message,
166 166
         :message_url => url_for(message.event_url)
167
    render_multipart('message_posted', body)
167
    render_multipart('message_posted', body, message.board.project)
168 168
  end
169 169
  
170 170
  # Builds a tmail object used to email the recipients of a project of the specified wiki content was added. 
......
181 181
    subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_added, :id => wiki_content.page.pretty_title)}"
182 182
    body :wiki_content => wiki_content,
183 183
         :wiki_content_url => url_for(:controller => 'wiki', :action => 'show', :project_id => wiki_content.project, :id => wiki_content.page.title)
184
    render_multipart('wiki_content_added', body)
184
    render_multipart('wiki_content_added', body, wiki_content.project)
185 185
  end
186 186
  
187 187
  # Builds a tmail object used to email the recipients of a project of the specified wiki content was updated. 
......
199 199
    body :wiki_content => wiki_content,
200 200
         :wiki_content_url => url_for(:controller => 'wiki', :action => 'show', :project_id => wiki_content.project, :id => wiki_content.page.title),
201 201
         :wiki_diff_url => url_for(:controller => 'wiki', :action => 'diff', :project_id => wiki_content.project, :id => wiki_content.page.title, :version => wiki_content.version)
202
    render_multipart('wiki_content_updated', body)
202
    render_multipart('wiki_content_updated', body, wiki_content.project)
203 203
  end
204 204

  
205 205
  # Builds a tmail object used to email the specified user their account information.
......
367 367
    if @author.pref[:no_self_notified]
368 368
      recipients.delete(@author.mail) if recipients
369 369
      cc.delete(@author.mail) if cc
370
    end
371
    
372
    notified_users = [recipients, cc].flatten.compact.uniq
373
    # Rails would log recipients only, not cc and bcc
374
    mylogger.info "Sending email notification to: #{notified_users.join(', ')}" if mylogger
375
    
376
    # Blind carbon copy recipients
377
    if Setting.bcc_recipients?
378
      bcc(notified_users)
379
      recipients []
380
      cc []
370
      bcc.delete(@author.mail) if bcc
381 371
    end
382 372
    super
383 373
  end
......
389 379
  # https://rails.lighthouseapp.com/projects/8994/tickets/2338-actionmailer-mailer-views-and-content-type
390 380
  # https://rails.lighthouseapp.com/projects/8994/tickets/1799-actionmailer-doesnt-set-template_format-when-rendering-layouts
391 381
  
392
  def render_multipart(method_name, body)
393
    if Setting.plain_text_mail?
382
  def render_multipart(method_name, body, project = nil)
383
    # Check whether project settings override global ones
384
    settings_hash = Hash.new
385
    if not project.nil?
386
      project.project_settings.each do |setting|
387
        settings_hash[setting.name] = setting
388
      end
389
    end
390
    from settings_hash['mail_from'].value if settings_hash['mail_from']
391
    notified_users = [recipients, cc].flatten.compact.uniq
392
    # Rails would log recipients only, not cc and bcc
393
    mylogger.info "Sending email notification to: #{notified_users.join(', ')}" if mylogger
394
    
395
    if (settings_hash.has_key?('bcc_recipients') and settings_hash['bcc_recipients'].value == '1') or Setting.bcc_recipients?
396
      bcc(notified_users)
397
      recipients []
398
      cc []
399
    end
400
    
401
    if settings_hash.has_key?('emails_header')
402
      body['emails_header'] = settings_hash['emails_header'].value
403
    else
404
      body['emails_header'] = Setting.emails_header
405
    end
406
    
407
    if settings_hash.has_key?('emails_footer')
408
      body['emails_footer'] = settings_hash['emails_footer'].value
409
    else
410
      body['emails_footer'] = Setting.emails_footer
411
    end
412

  
413
    if (settings_hash.has_key?('plain_text_mail') and settings_hash['plain_text_mail'].value == '1') or Setting.plain_text_mail?
394 414
      content_type "text/plain"
395 415
      body render(:file => "#{method_name}.text.plain.rhtml", :body => body, :layout => 'mailer.text.plain.erb')
396 416
    else
app/models/project_setting.rb (revision 0)
1
class ProjectSetting < ActiveRecord::Base
2
  belongs_to :project
3
  # Possible setting names
4
  NAMES = ['mail_from', 'bcc_recipients', 'plain_text_mail', 'emails_header', 'emails_footer']
5

  
6
  def self.copy_settings(project)
7
    self::NAMES.each do |name|
8
      setting = Setting[name]
9
      self.new({ :name => name, :value => setting, :project => project }).save
10
    end
11
  end
12
end
app/models/project.rb (working copy)
48 48
  has_many :boards, :dependent => :destroy, :order => "position ASC"
49 49
  has_one :repository, :dependent => :destroy
50 50
  has_many :changesets, :through => :repository
51
  has_many :project_settings
51 52
  has_one :wiki, :dependent => :destroy
52 53
  # Custom field for the project issues
53 54
  has_and_belongs_to_many :issue_custom_fields, 
app/controllers/projects_controller.rb (working copy)
83 83
        m = Member.new(:user => User.current, :roles => [r])
84 84
        @project.members << m
85 85
      end
86
      # Copy email settings from global ones
87
      ProjectSetting.copy_settings @project
86 88
      respond_to do |format|
87 89
        format.html { 
88 90
          flash[:notice] = l(:notice_successful_create)
......
120 122
        @project.enabled_module_names = params[:enabled_modules]
121 123
        if validate_parent_id && @project.copy(@source_project, :only => params[:only])
122 124
          @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
125
          # Copy email settings from global ones
126
          ProjectSetting.copy_settings @project
123 127
          flash[:notice] = l(:notice_successful_create)
124 128
          redirect_to :controller => 'projects', :action => 'settings', :id => @project
125 129
        elsif !@project.new_record?
......
176 180
    @trackers = Tracker.all
177 181
    @repository ||= @project.repository
178 182
    @wiki ||= @project.wiki
183
    @deliveries = ActionMailer::Base.perform_deliveries
179 184
  end
180 185
  
181 186
  def edit
......
243 248
    # hide project in layout
244 249
    @project = nil
245 250
  end
251
  
252
  verify :method => :post, :only => :notifications, :render => {:nothing => true, :status => :method_not_allowed }
253
  def notifications
254
    if request.post? && params[:settings] && params[:settings].is_a?(Hash)
255
      settings_hash = Hash.new
256
      @project.project_settings.each do |setting|
257
        settings_hash[setting.name] = setting
258
      end
259
      params[:settings].each do |name, value|
260
        if settings_hash.has_key?(name)
261
          settings_hash[name].value = value
262
          settings_hash[name].save
263
        end
264
      end
265
      flash[:notice] = l(:notice_successful_update)
266
      redirect_to :action => 'settings', :id => @project, :tab => 'notifications'
267
    end
268
  end
246 269

  
247 270
private
248 271
  def find_optional_project
app/views/layouts/mailer.text.plain.erb (working copy)
1
<%= Setting.emails_header %>
1
<%= @emails_header %>
2 2
<%= yield %>
3 3
-- 
4
<%= Setting.emails_footer %>
4
<%= @emails_footer %>
app/views/layouts/mailer.text.html.erb (working copy)
25 25
</style>
26 26
</head>
27 27
<body>
28
<span class="header"><%= Redmine::WikiFormatting.to_html(Setting.text_formatting, Setting.emails_header) %></span>
28
<span class="header"><%= Redmine::WikiFormatting.to_html(Setting.text_formatting, @emails_header) %></span>
29 29
<%= yield %>
30 30
<hr />
31
<span class="footer"><%= Redmine::WikiFormatting.to_html(Setting.text_formatting, Setting.emails_footer) %></span>
31
<span class="footer"><%= Redmine::WikiFormatting.to_html(Setting.text_formatting, @emails_footer) %></span>
32 32
</body>
33 33
</html>
app/views/projects/settings/_notifications.erb (revision 0)
1
<% if @deliveries %>
2
<% form_for :project, @project,
3
            :url => { :action => 'notifications', :id => @project },
4
            :html => {:id => 'notifications-form'} do |f| %>
5

  
6
<div class="box tabular settings">
7
<p><%= setting_text_field :mail_from, :size => 60 %></p>
8

  
9
<p><%= setting_check_box :bcc_recipients %></p>
10

  
11
<p><%= setting_check_box :plain_text_mail %></p>
12

  
13
</div>
14

  
15
<fieldset class="box"><legend><%= l(:setting_emails_header) %></legend>
16
<%= setting_text_area :emails_header, :label => false, :class => 'wiki-edit', :rows => 5 %>
17
</fieldset>
18

  
19
<fieldset class="box"><legend><%= l(:setting_emails_footer) %></legend>
20
<%= setting_text_area :emails_footer, :label => false, :class => 'wiki-edit', :rows => 5 %>
21
</fieldset>
22

  
23
<%= submit_tag l(:button_save) %>
24
<% end %>
25
<% else %>
26
<div class="nodata">
27
<%= simple_format(l(:text_email_delivery_not_configured)) %>
28
</div>
29
<% end %>
db/migrate/20110201111915_copy_email_settings.rb (revision 0)
1
class CopyEmailSettings < ActiveRecord::Migration
2
  # Copy email settings from global ones into ProjectSetting
3
  def self.up
4
    Project.all.each do |project|
5
      ProjectSetting.copy_settings project
6
    end
7
  end
8

  
9
  def self.down
10
    ProjectSetting.delete_all
11
  end
12
end
db/migrate/20110201104137_create_project_settings.rb (revision 0)
1
class CreateProjectSettings < ActiveRecord::Migration
2
  def self.up
3
    create_table :project_settings do |t|
4
      t.string :name
5
      t.string :value
6
      t.references :project
7

  
8
      t.timestamps
9
    end
10
  end
11

  
12
  def self.down
13
    drop_table :project_settings
14
  end
15
end
config/locales/en.yml (working copy)
369 369
  permission_select_project_modules: Select project modules
370 370
  permission_manage_members: Manage members
371 371
  permission_manage_project_activities: Manage project activities
372
  permission_manage_project_notifications: Manage project notification settings
372 373
  permission_manage_versions: Manage versions
373 374
  permission_manage_categories: Manage issue categories
374 375
  permission_view_issues: View Issues
config/routes.rb (working copy)
139 139
    :settings => :get,
140 140
    :modules => :post,
141 141
    :archive => :post,
142
    :unarchive => :post
142
    :unarchive => :post,
143
    :notifications => :post
143 144
  } do |project|
144 145
    project.resource :project_enumerations, :as => 'enumerations', :only => [:update, :destroy]
145 146
    project.resources :files, :only => [:index, :new, :create]
lib/redmine.rb (working copy)
49 49
  map.permission :search_project, {:search => :index}, :public => true
50 50
  map.permission :add_project, {:projects => [:new, :create]}, :require => :loggedin
51 51
  map.permission :edit_project, {:projects => [:settings, :edit, :update]}, :require => :member
52
  map.permission :manage_project_notifications, {:projects => :notifications}, :require => :member
52 53
  map.permission :select_project_modules, {:projects => :modules}, :require => :member
53 54
  map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy, :autocomplete_for_member]}, :require => :member
54 55
  map.permission :manage_versions, {:projects => :settings, :versions => [:new, :create, :edit, :update, :close_completed, :destroy]}, :require => :member
(2-2/3)