Add digest e-mails
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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? | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -2,7 +2,6 @@ | ||||
|  | ||||
| class Setting < RailsSettings::Base | ||||
|   source Rails.root.join('config/settings.yml') | ||||
|   namespace Rails.env | ||||
|  | ||||
|   def to_param | ||||
|     var | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <%= yield %> | ||||
|  | ||||
| --- | ||||
|  | ||||
| <%= t('application_mailer.signature', instance: Rails.configuration.x.local_domain) %> | ||||
| <%= t('application_mailer.settings', link: settings_preferences_url) %> | ||||
|   | ||||
| @@ -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}") %> | ||||
|   | ||||
							
								
								
									
										15
									
								
								app/views/notification_mailer/digest.text.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								app/views/notification_mailer/digest.text.erb
									
									
									
									
									
										Normal file
									
								
							| @@ -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 %> | ||||
| @@ -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 } %> | ||||
|   | ||||
| @@ -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}") %> | ||||
|   | ||||
| @@ -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") %> | ||||
|   | ||||
| @@ -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 } %> | ||||
|   | ||||
| @@ -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 } %> | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										14
									
								
								app/workers/digest_mailer_worker.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/workers/digest_mailer_worker.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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: "*" | ||||
|   | ||||
| @@ -11,6 +11,7 @@ defaults: &defaults | ||||
|     favourite: false | ||||
|     mention: false | ||||
|     follow_request: true | ||||
|     digest: true | ||||
|   interactions: | ||||
|     must_be_follower: false | ||||
|     must_be_following: false | ||||
|   | ||||
| @@ -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 | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user