about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2017-03-03 23:45:48 +0100
committerEugen Rochko <eugen@zeonfederated.com>2017-03-04 00:00:48 +0100
commit6b81d100306259cd17b38d3f0f9dec0f0fb5b5d9 (patch)
treea6d40da1644804061eaeacdfd748d027aac2c41b
parentf5457cc3d2be79525d339dd5a5c046de6d0acf9d (diff)
Add digest e-mails
-rw-r--r--app/controllers/api_controller.rb1
-rw-r--r--app/controllers/settings/preferences_controller.rb3
-rw-r--r--app/lib/formatter.rb5
-rw-r--r--app/mailers/notification_mailer.rb13
-rw-r--r--app/models/setting.rb1
-rw-r--r--app/models/user.rb7
-rw-r--r--app/views/layouts/mailer.text.erb2
-rw-r--r--app/views/notification_mailer/_status.text.erb4
-rw-r--r--app/views/notification_mailer/digest.text.erb15
-rw-r--r--app/views/notification_mailer/favourite.text.erb4
-rw-r--r--app/views/notification_mailer/follow.text.erb4
-rw-r--r--app/views/notification_mailer/follow_request.text.erb4
-rw-r--r--app/views/notification_mailer/mention.text.erb4
-rw-r--r--app/views/notification_mailer/reblog.text.erb4
-rw-r--r--app/views/settings/preferences/show.html.haml1
-rw-r--r--app/workers/digest_mailer_worker.rb14
-rw-r--r--config/application.rb7
-rw-r--r--config/environments/production.rb7
-rw-r--r--config/locales/en.yml11
-rw-r--r--config/locales/simple_form.en.yml1
-rw-r--r--config/settings.yml1
-rw-r--r--db/migrate/20170303212857_add_last_emailed_at_to_users.rb5
-rw-r--r--db/schema.rb3
-rw-r--r--lib/tasks/mastodon.rake11
-rw-r--r--spec/mailers/previews/notification_mailer_preview.rb17
25 files changed, 117 insertions, 32 deletions
diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb
index c2002cb79..db16f82e5 100644
--- a/app/controllers/api_controller.rb
+++ b/app/controllers/api_controller.rb
@@ -79,6 +79,7 @@ class ApiController < ApplicationController
 
   def require_user!
     current_resource_owner
+    set_user_activity
   rescue ActiveRecord::RecordNotFound
     render json: { error: 'This method requires an authenticated user' }, status: 422
   end
diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb
index b7479bf8c..60400e465 100644
--- a/app/controllers/settings/preferences_controller.rb
+++ b/app/controllers/settings/preferences_controller.rb
@@ -14,6 +14,7 @@ class Settings::PreferencesController < ApplicationController
       reblog:         user_params[:notification_emails][:reblog]         == '1',
       favourite:      user_params[:notification_emails][:favourite]      == '1',
       mention:        user_params[:notification_emails][:mention]        == '1',
+      digest:         user_params[:notification_emails][:digest]         == '1',
     }
 
     current_user.settings['interactions'] = {
@@ -33,6 +34,6 @@ class Settings::PreferencesController < ApplicationController
   private
 
   def user_params
-    params.require(:user).permit(:locale, :setting_default_privacy, notification_emails: [:follow, :follow_request, :reblog, :favourite, :mention], interactions: [:must_be_follower, :must_be_following])
+    params.require(:user).permit(:locale, :setting_default_privacy, notification_emails: [:follow, :follow_request, :reblog, :favourite, :mention, :digest], interactions: [:must_be_follower, :must_be_following])
   end
 end
diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb
index e353c3504..b58952ae0 100644
--- a/app/lib/formatter.rb
+++ b/app/lib/formatter.rb
@@ -29,6 +29,11 @@ class Formatter
     sanitize(html, tags: %w(a br p span), attributes: %w(href rel class))
   end
 
+  def plaintext(status)
+    return status.text if status.local?
+    strip_tags(status.text)
+  end
+
   def simplified_format(account)
     return reformat(account.note) unless account.local?
 
diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb
index a1b084682..bf4c16e43 100644
--- a/app/mailers/notification_mailer.rb
+++ b/app/mailers/notification_mailer.rb
@@ -49,4 +49,17 @@ class NotificationMailer < ApplicationMailer
       mail to: @me.user.email, subject: I18n.t('notification_mailer.follow_request.subject', name: @account.acct)
     end
   end
+
+  def digest(recipient, opts = {})
+    @me            = recipient
+    @since         = opts[:since] || @me.user.last_emailed_at || @me.user.current_sign_in_at
+    @notifications = Notification.where(account: @me, activity_type: 'Mention').where('created_at > ?', @since)
+    @follows_since = Notification.where(account: @me, activity_type: 'Follow').where('created_at > ?', @since).count
+
+    return if @notifications.empty?
+
+    I18n.with_locale(@me.user.locale || I18n.default_locale) do
+      mail to: @me.user.email, subject: I18n.t('notification_mailer.digest.subject', count: @notifications.size)
+    end
+  end
 end
diff --git a/app/models/setting.rb b/app/models/setting.rb
index 3796253d4..31e1ee198 100644
--- a/app/models/setting.rb
+++ b/app/models/setting.rb
@@ -2,7 +2,6 @@
 
 class Setting < RailsSettings::Base
   source Rails.root.join('config/settings.yml')
-  namespace Rails.env
 
   def to_param
     var
diff --git a/app/models/user.rb b/app/models/user.rb
index 08aac2679..bf2916d90 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -14,9 +14,10 @@ class User < ApplicationRecord
   validates :locale, inclusion: I18n.available_locales.map(&:to_s), unless: 'locale.nil?'
   validates :email, email: true
 
-  scope :prolific, -> { joins('inner join statuses on statuses.account_id = users.account_id').select('users.*, count(statuses.id) as statuses_count').group('users.id').order('statuses_count desc') }
-  scope :recent,   -> { order('id desc') }
-  scope :admins,   -> { where(admin: true) }
+  scope :prolific,  -> { joins('inner join statuses on statuses.account_id = users.account_id').select('users.*, count(statuses.id) as statuses_count').group('users.id').order('statuses_count desc') }
+  scope :recent,    -> { order('id desc') }
+  scope :admins,    -> { where(admin: true) }
+  scope :confirmed, -> { where.not(confirmed_at: nil) }
 
   def send_devise_notification(notification, *args)
     devise_mailer.send(notification, self, *args).deliver_later
diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb
index ae52173b5..21bf444c3 100644
--- a/app/views/layouts/mailer.text.erb
+++ b/app/views/layouts/mailer.text.erb
@@ -1,5 +1,5 @@
 <%= yield %>
-
 ---
 
 <%= t('application_mailer.signature', instance: Rails.configuration.x.local_domain) %>
+<%= t('application_mailer.settings', link: settings_preferences_url) %>
diff --git a/app/views/notification_mailer/_status.text.erb b/app/views/notification_mailer/_status.text.erb
index b089a7b73..85a0136b7 100644
--- a/app/views/notification_mailer/_status.text.erb
+++ b/app/views/notification_mailer/_status.text.erb
@@ -1,3 +1,3 @@
-<%= strip_tags(@status.content) %>
+<%= raw Formatter.instance.plaintext(status) %>
 
-<%= web_url("statuses/#{@status.id}") %>
+<%= raw t('application_mailer.view')%> <%= web_url("statuses/#{status.id}") %>
diff --git a/app/views/notification_mailer/digest.text.erb b/app/views/notification_mailer/digest.text.erb
new file mode 100644
index 000000000..95aed6793
--- /dev/null
+++ b/app/views/notification_mailer/digest.text.erb
@@ -0,0 +1,15 @@
+<%= display_name(@me) %>,
+
+<%= raw t('notification_mailer.digest.body', since: @since, instance: root_url) %>
+<% @notifications.each do |notification| %>
+
+* <%= raw t('notification_mailer.digest.mention', name: notification.from_account.acct) %>
+
+  <%= raw Formatter.instance.plaintext(notification.target_status) %>
+
+  <%= raw t('application_mailer.view')%> <%= web_url("statuses/#{notification.target_status.id}") %>
+<% end %>
+<% if @follows_since > 0 %>
+
+<%= raw t('notification_mailer.digest.new_followers_summary', count: @follows_since) %>
+<% end %>
diff --git a/app/views/notification_mailer/favourite.text.erb b/app/views/notification_mailer/favourite.text.erb
index b2e1e3e9e..99852592f 100644
--- a/app/views/notification_mailer/favourite.text.erb
+++ b/app/views/notification_mailer/favourite.text.erb
@@ -1,5 +1,5 @@
 <%= display_name(@me) %>,
 
-<%= t('notification_mailer.favourite.body', name: @account.acct) %>
+<%= raw t('notification_mailer.favourite.body', name: @account.acct) %>
 
-<%= render partial: 'status' %>
+<%= render partial: 'status', locals: { status: @status } %>
diff --git a/app/views/notification_mailer/follow.text.erb b/app/views/notification_mailer/follow.text.erb
index 4b2ec142c..af41a3080 100644
--- a/app/views/notification_mailer/follow.text.erb
+++ b/app/views/notification_mailer/follow.text.erb
@@ -1,5 +1,5 @@
 <%= display_name(@me) %>,
 
-<%= t('notification_mailer.follow.body', name: @account.acct) %>
+<%= raw t('notification_mailer.follow.body', name: @account.acct) %>
 
-<%= web_url("accounts/#{@account.id}") %>
+<%= raw t('application_mailer.view')%> <%= web_url("accounts/#{@account.id}") %>
diff --git a/app/views/notification_mailer/follow_request.text.erb b/app/views/notification_mailer/follow_request.text.erb
index c0d38ec67..49087a575 100644
--- a/app/views/notification_mailer/follow_request.text.erb
+++ b/app/views/notification_mailer/follow_request.text.erb
@@ -1,5 +1,5 @@
 <%= display_name(@me) %>,
 
-<%= t('notification_mailer.follow_request.body', name: @account.acct) %>
+<%= raw t('notification_mailer.follow_request.body', name: @account.acct) %>
 
-<%= web_url("follow_requests") %>
+<%= raw t('application_mailer.view')%> <%= web_url("follow_requests") %>
diff --git a/app/views/notification_mailer/mention.text.erb b/app/views/notification_mailer/mention.text.erb
index 31a294bb9..c0d4be1d8 100644
--- a/app/views/notification_mailer/mention.text.erb
+++ b/app/views/notification_mailer/mention.text.erb
@@ -1,5 +1,5 @@
 <%= display_name(@me) %>,
 
-<%= t('notification_mailer.mention.body', name: @status.account.acct) %>
+<%= raw t('notification_mailer.mention.body', name: @status.account.acct) %>
 
-<%= render partial: 'status' %>
+<%= render partial: 'status', locals: { status: @status } %>
diff --git a/app/views/notification_mailer/reblog.text.erb b/app/views/notification_mailer/reblog.text.erb
index 7af8052ca..c32b48650 100644
--- a/app/views/notification_mailer/reblog.text.erb
+++ b/app/views/notification_mailer/reblog.text.erb
@@ -1,5 +1,5 @@
 <%= display_name(@me) %>,
 
-<%= t('notification_mailer.reblog.body', name: @account.acct) %>
+<%= raw t('notification_mailer.reblog.body', name: @account.acct) %>
 
-<%= render partial: 'status' %>
+<%= render partial: 'status', locals: { status: @status } %>
diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml
index aee0540d2..a17279b1e 100644
--- a/app/views/settings/preferences/show.html.haml
+++ b/app/views/settings/preferences/show.html.haml
@@ -16,6 +16,7 @@
       = ff.input :reblog, as: :boolean, wrapper: :with_label
       = ff.input :favourite, as: :boolean, wrapper: :with_label
       = ff.input :mention, as: :boolean, wrapper: :with_label
+      = ff.input :digest, as: :boolean, wrapper: :with_label
 
   = f.simple_fields_for :interactions, hash_to_object(current_user.settings.interactions) do |ff|
     = ff.input :must_be_follower, as: :boolean, wrapper: :with_label
diff --git a/app/workers/digest_mailer_worker.rb b/app/workers/digest_mailer_worker.rb
new file mode 100644
index 000000000..dedb21e4e
--- /dev/null
+++ b/app/workers/digest_mailer_worker.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class DigestMailerWorker
+  include Sidekiq::Worker
+
+  sidekiq_options queue: 'mailers'
+
+  def perform(user_id)
+    user = User.find(user_id)
+    return unless user.settings.notification_emails['digest']
+    NotificationMailer.digest(user.account).deliver_now!
+    user.touch(:last_emailed_at)
+  end
+end
diff --git a/config/application.rb b/config/application.rb
index 8da5ade3c..1ea65619c 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -49,12 +49,5 @@ module Mastodon
       Doorkeeper::AuthorizedApplicationsController.layout 'admin'
       Doorkeeper::Application.send :include, ApplicationExtension
     end
-
-    config.action_dispatch.default_headers = {
-      'Server'                 => 'Mastodon',
-      'X-Frame-Options'        => 'DENY',
-      'X-Content-Type-Options' => 'nosniff',
-      'X-XSS-Protection'       => '1; mode=block',
-    }
   end
 end
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 67ff63914..dc5dd4afd 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -109,4 +109,11 @@ Rails.application.configure do
   config.to_prepare do
     StatsD.backend = StatsD::Instrument::Backends::NullBackend.new if ENV['STATSD_ADDR'].blank?
   end
+
+  config.action_dispatch.default_headers = {
+    'Server'                 => 'Mastodon',
+    'X-Frame-Options'        => 'DENY',
+    'X-Content-Type-Options' => 'nosniff',
+    'X-XSS-Protection'       => '1; mode=block',
+  }
 end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 6da30acda..f11a689e4 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -29,6 +29,8 @@ en:
     unfollow: Unfollow
   application_mailer:
     signature: Mastodon notifications from %{instance}
+    settings: 'Change e-mail preferences: %{link}'
+    view: 'View:'
   applications:
     invalid_url: The provided URL is invalid
   auth:
@@ -83,6 +85,15 @@ en:
     reblog:
       body: 'Your status was boosted by %{name}:'
       subject: "%{name} boosted your status"
+    digest:
+      subject:
+        one: "1 new notification since your last visit 🐘"
+        other: "%{count} new notifications since your last visit 🐘"
+      body: 'Here is a brief summary of what you missed on %{instance} since your last visit on %{since}:'
+      mention: "%{name} mentioned you in:"
+      new_followers_summary:
+        one: You have acquired one new follower! Yay!
+        other: You have gotten %{count} new followers! Amazing!
   pagination:
     next: Next
     prev: Prev
diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml
index 4d1758f82..170af01cf 100644
--- a/config/locales/simple_form.en.yml
+++ b/config/locales/simple_form.en.yml
@@ -34,6 +34,7 @@ en:
         follow_request: Send e-mail when someone requests to follow you
         mention: Send e-mail when someone mentions you
         reblog: Send e-mail when someone reblogs your status
+        digest: Send digest e-mails
     'no': 'No'
     required:
       mark: "*"
diff --git a/config/settings.yml b/config/settings.yml
index 71ce12e63..6ae9217a4 100644
--- a/config/settings.yml
+++ b/config/settings.yml
@@ -11,6 +11,7 @@ defaults: &defaults
     favourite: false
     mention: false
     follow_request: true
+    digest: true
   interactions:
     must_be_follower: false
     must_be_following: false
diff --git a/db/migrate/20170303212857_add_last_emailed_at_to_users.rb b/db/migrate/20170303212857_add_last_emailed_at_to_users.rb
new file mode 100644
index 000000000..9ae3da4fb
--- /dev/null
+++ b/db/migrate/20170303212857_add_last_emailed_at_to_users.rb
@@ -0,0 +1,5 @@
+class AddLastEmailedAtToUsers < ActiveRecord::Migration[5.0]
+  def change
+    add_column :users, :last_emailed_at, :datetime, null: true, default: nil
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index c2d88ac13..8cc3bd8e3 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20170301222600) do
+ActiveRecord::Schema.define(version: 20170303212857) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -283,6 +283,7 @@ ActiveRecord::Schema.define(version: 20170301222600) do
     t.string   "encrypted_otp_secret_salt"
     t.integer  "consumed_timestep"
     t.boolean  "otp_required_for_login"
+    t.datetime "last_emailed_at"
     t.index ["account_id"], name: "index_users_on_account_id", using: :btree
     t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
     t.index ["email"], name: "index_users_on_email", unique: true, using: :btree
diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake
index 8482d4124..bb10410b5 100644
--- a/lib/tasks/mastodon.rake
+++ b/lib/tasks/mastodon.rake
@@ -43,7 +43,7 @@ namespace :mastodon do
   namespace :feeds do
     desc 'Clear timelines of inactive users'
     task clear: :environment do
-      User.where('current_sign_in_at < ?', 14.days.ago).find_each do |user|
+      User.confirmed.where('current_sign_in_at < ?', 14.days.ago).find_each do |user|
         Redis.current.del(FeedManager.instance.key(:home, user.account_id))
       end
     end
@@ -53,4 +53,13 @@ namespace :mastodon do
       Redis.current.keys('feed:*').each { |key| Redis.current.del(key) }
     end
   end
+
+  namespace :emails do
+    desc 'Send out digest e-mails'
+    task digest: :environment do
+      User.confirmed.joins(:account).where(accounts: { silenced: false, suspended: false }).where('current_sign_in_at < ?', 20.days.ago).find_each do |user|
+        DigestMailerWorker.perform_async(user.id)
+      end
+    end
+  end
 end
diff --git a/spec/mailers/previews/notification_mailer_preview.rb b/spec/mailers/previews/notification_mailer_preview.rb
index 8fc8d0d34..a08a80d17 100644
--- a/spec/mailers/previews/notification_mailer_preview.rb
+++ b/spec/mailers/previews/notification_mailer_preview.rb
@@ -1,24 +1,31 @@
 # Preview all emails at http://localhost:3000/rails/mailers/notification_mailer
 class NotificationMailerPreview < ActionMailer::Preview
-
   # Preview this email at http://localhost:3000/rails/mailers/notification_mailer/mention
   def mention
-    # NotificationMailer.mention
+    m = Mention.last
+    NotificationMailer.mention(m.account, Notification.find_by(activity: m))
   end
 
   # Preview this email at http://localhost:3000/rails/mailers/notification_mailer/follow
   def follow
-    # NotificationMailer.follow
+    f = Follow.last
+    NotificationMailer.follow(f.target_account, Notification.find_by(activity: f))
   end
 
   # Preview this email at http://localhost:3000/rails/mailers/notification_mailer/favourite
   def favourite
-    # NotificationMailer.favourite
+    f = Favourite.last
+    NotificationMailer.favourite(f.status.account, Notification.find_by(activity: f))
   end
 
   # Preview this email at http://localhost:3000/rails/mailers/notification_mailer/reblog
   def reblog
-    # NotificationMailer.reblog
+    r = Status.where.not(reblog_of_id: nil).first
+    NotificationMailer.reblog(r.reblog.account, Notification.find_by(activity: r))
   end
 
+  # Preview this email at http://localhost:3000/rails/mailers/notification_mailer/digest
+  def digest
+    NotificationMailer.digest(Account.first, since: 90.days.ago)
+  end
 end