Merge pull request #3326 from consul/admin_system_emails

[Backport] Show all system emails in Admin section
This commit is contained in:
Julian Nicolas Herrero
2019-02-26 15:18:42 +01:00
committed by GitHub
11 changed files with 484 additions and 41 deletions

View File

@@ -410,6 +410,10 @@ $sidebar-active: #f4fcd0;
}
}
code {
word-break: break-all;
}
// 02. Sidebar
// -----------

View File

@@ -4,15 +4,36 @@ class Admin::SystemEmailsController < Admin::BaseController
def index
@system_emails = {
proposal_notification_digest: %w(view preview_pending)
proposal_notification_digest: %w[view preview_pending],
budget_investment_created: %w[view edit_info],
budget_investment_selected: %w[view edit_info],
budget_investment_unfeasible: %w[view edit_info],
budget_investment_unselected: %w[view edit_info],
comment: %w[view edit_info],
reply: %w[view edit_info],
direct_message_for_receiver: %w[view edit_info],
direct_message_for_sender: %w[view edit_info],
email_verification: %w[view edit_info],
user_invite: %w[view edit_info]
}
end
def view
case @system_email
when "proposal_notification_digest"
@notifications = Notification.where(notifiable_type: "ProposalNotification").limit(2)
@subject = t('mailers.proposal_notification_digest.title', org_name: Setting['org_name'])
load_sample_proposal_notifications
when /\Abudget_investment/
load_sample_investment
when /\Adirect_message/
load_sample_direct_message
when "comment"
load_sample_comment
when "reply"
load_sample_reply
when "email_verification"
load_sample_user
when "user_invite"
@subject = t("mailers.user_invite.subject", org_name: Setting["org_name"])
end
end
@@ -39,12 +60,62 @@ class Admin::SystemEmailsController < Admin::BaseController
private
def load_system_email
@system_email = params[:system_email_id]
end
def load_system_email
@system_email = params[:system_email_id]
end
def unsent_proposal_notifications_ids
Notification.where(notifiable_type: "ProposalNotification", emailed_at: nil)
.group(:notifiable_id).count.keys
end
def load_sample_proposal_notifications
@notifications = Notification.where(notifiable_type: "ProposalNotification").limit(2)
@subject = t("mailers.proposal_notification_digest.title", org_name: Setting["org_name"])
end
def load_sample_investment
if Budget::Investment.any?
@investment = Budget::Investment.last
@subject = t("mailers.#{@system_email}.subject", code: @investment.code)
else
redirect_to admin_system_emails_path, alert: t("admin.system_emails.alert.no_investments")
end
end
def load_sample_comment
@comment = Comment.where(commentable_type: %w[Debate Proposal Budget::Investment]).last
if @comment
@commentable = @comment.commentable
@subject = t("mailers.comment.subject", commentable: commentable_name)
else
redirect_to admin_system_emails_path, alert: t("admin.system_emails.alert.no_comments")
end
end
def load_sample_reply
reply = Comment.select { |comment| comment.reply? }.last
if reply
@email = ReplyEmail.new(reply)
else
redirect_to admin_system_emails_path, alert: t("admin.system_emails.alert.no_replies")
end
end
def load_sample_user
@user = User.last
@token = @user.email_verification_token || SecureRandom.hex
@subject = t("mailers.email_verification.subject")
end
def load_sample_direct_message
@direct_message = DirectMessage.new(sender: current_user, receiver: current_user,
title: t("admin.system_emails.message_title"),
body: t("admin.system_emails.message_body"))
@subject = t("mailers.#{@system_email}.subject")
end
def commentable_name
t("activerecord.models.#{@commentable.class.name.underscore}", count: 1)
end
def unsent_proposal_notifications_ids
Notification.where(notifiable_type: "ProposalNotification", emailed_at: nil)
.group(:notifiable_id).count.keys
end
end

View File

@@ -17,14 +17,11 @@ class Mailer < ApplicationMailer
end
def reply(reply)
@reply = reply
@commentable = @reply.commentable
parent = Comment.find(@reply.parent_id)
@recipient = parent.author
@email_to = @recipient.email
@email = ReplyEmail.new(reply)
@email_to = @email.to
with_user(@recipient) do
mail(to: @email_to, subject: t('mailers.reply.subject')) if @commentable.present? && @recipient.present?
with_user(@email.recipient) do
mail(to: @email_to, subject: @email.subject) if @email.can_be_sent?
end
end

View File

@@ -24,19 +24,27 @@
class: "button hollow expanded" %>
</div>
<% end %>
<% if system_email_actions.include?('preview_pending') %>
<% if system_email_actions.include?("preview_pending") %>
<div class="small-4 column">
<%= link_to t("admin.system_emails.preview_pending.action"),
admin_system_email_preview_pending_path(system_email_title),
class: "button expanded" %>
</div>
<div class="small-4 column">
<%= link_to t("admin.system_emails.preview_pending.send_pending"),
admin_system_email_send_pending_path(system_email_title),
class: "button success expanded",
method: :put %>
</div>
<% end %>
<% if system_email_actions.include?("edit_info") %>
<div class="small-8 column">
<p class="help-text">
<%= t("admin.system_emails.edit_info") %><br>
<code><%= "app/views/mailer/#{system_email_title}.html.erb" %></code>
</p>
</div>
<% end %>
<div class="small-4 column">
<%= link_to t("admin.system_emails.preview_pending.send_pending"),
admin_system_email_send_pending_path(system_email_title),
class: "button success expanded",
method: :put %>
</div>
</td>
</tr>
<% end %>

View File

@@ -11,7 +11,7 @@
</div>
<div class="small-12 medium-8 column">
<strong><%= t("admin.newsletters.show.subject") %></strong><br>
<%= @subject %>
<%= @subject || @email.subject %>
</div>
</div>
</div>

View File

@@ -5,16 +5,16 @@
</h1>
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 14px;font-weight: normal;line-height: 24px;">
<%= t("mailers.reply.hi") %> <strong><%= @recipient.name %></strong>,
<%= t("mailers.reply.hi") %> <strong><%= @email.recipient.name %></strong>,
</p>
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 14px;font-weight: normal;line-height: 24px;">
<%= t("mailers.reply.new_reply_by_html", commenter: @reply.author.name) %> <%= link_to @commentable.title, comment_url(@reply.id), style: "color: #2895F1; text-decoration:none;" %>
<%= t("mailers.reply.new_reply_by_html", commenter: @email.reply.author.name) %> <%= link_to @email.commentable.title, comment_url(@email.reply.id), style: "color: #2895F1; text-decoration:none;" %>
</p>
<p style="border-left: 2px solid #DEE0E3;font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 14px;font-style: italic;font-weight: normal;line-height: 24px;margin-left: 20px;padding: 10px;">
<%= text_with_links @reply.body %>
</p>
<div style="border-left: 2px solid #DEE0E3;font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 14px;font-style: italic;font-weight: normal;line-height: 24px;margin-left: 20px;padding: 10px;">
<%= simple_format text_with_links(@email.reply.body), {}, sanitize: false %>
</div>
<p style="font-family: 'Open Sans','Helvetica Neue',arial,sans-serif;font-size: 12px;font-weight: normal;line-height: 20px;">
<%= t("mailers.config.manage_email_subscriptions") %> <%= link_to t("account.show.title"), account_url, style: "color: #2895F1; text-decoration:none;" %>

View File

@@ -762,9 +762,46 @@ en:
send_pending: Send pending
send_pending_notification: Pending notifications sent succesfully
proposal_notification_digest:
title: Proposal Notification Digest
description: Gathers all proposal notifications for an user in a single message, to avoid too much emails.
preview_detail: Users will only recieve notifications from the proposals they are following
title: "Proposal notification digest"
description: "Gathers all proposal notifications for an user in a single message, to avoid too much emails."
preview_detail: "Users will only recieve notifications from the proposals they are following"
budget_investment_created:
title: "Budget investment created"
description: "Sent when a user creates a budget investment."
budget_investment_selected:
title: "Budget investment selected"
description: "Sent to the author when its budget investment has been selected."
budget_investment_unfeasible:
title: "Budget investment unfeasible"
description: "Sent to the author when its budget investment has been marked as unfeasible."
budget_investment_unselected:
title: "Budget investment unselected"
description: "Sent to the author when its budget investment hasn't been selected for voting phase."
comment:
title: "Comment"
description: "Sent to the author when recieves a comment."
reply:
title: "Reply"
description: "Sent to the comment's author when recieves a reply."
direct_message_for_receiver:
title: "Private message receiver"
description: "Sent to the private message's receiver."
direct_message_for_sender:
title: "Private message sender"
description: "Sent to the private message's sender."
email_verification:
title: "Email verification"
description: "Sent to a new user to verify its account."
user_invite:
title: "User Invitation"
description: "Sent to the person that has been invited to register an account."
edit_info: "You can edit this email in"
message_title: "Message's Title"
message_body: "This is a sample of message's content."
alert:
no_investments: "There aren't any budget investment created. Some example data is needed in order to preview the email."
no_comments: "There aren't any comments created. Some example data is needed in order to preview the email."
no_replies: "There aren't any replies created. Some example data is needed in order to preview the email."
emails_download:
index:
title: Emails download

View File

@@ -761,9 +761,46 @@ es:
send_pending: Enviar pendientes
send_pending_notification: Notificaciones pendientes enviadas correctamente
proposal_notification_digest:
title: Resumen de Notificationes de Propuestas
description: Reune todas las notificaciones de propuestas en un único mensaje, para evitar demasiados emails.
preview_detail: Los usuarios sólo recibirán las notificaciones de aquellas propuestas que siguen.
title: "Resumen de notificationes de propuestas"
description: "Reune todas las notificaciones de propuestas en un único mensaje, para evitar demasiados emails."
preview_detail: "Los usuarios sólo recibirán las notificaciones de aquellas propuestas que siguen."
budget_investment_created:
title: "Proyecto de gasto creado"
description: "Enviado cuando un usuario crea un proyecto de presupuestos participativos."
budget_investment_selected:
title: "Proyecto de gasto seleccionado"
description: "Enviado al autor de un proyecto de presupuestos participativos que ha sido seleccionado."
budget_investment_unfeasible:
title: "Proyecto de gasto inviable"
description: "Enviado al autor de un proyecto de presupuestos participativos que ha sido marcado como inviable."
budget_investment_unselected:
title: "Proyecto de gasto no seleccionado"
description: "Enviado al autor de un proyecto de presupuestos participativos que no ha sido seleccionado para la fase de votación."
comment:
title: "Comentario"
description: "Enviado al autor cuando recibe un comentario."
reply:
title: "Respuesta"
description: "Enviado al autor del comentario cuando recibe una respuesta."
direct_message_for_receiver:
title: "Mensaje privado recibido"
description: "Enviado al receptor de un mensaje privado."
direct_message_for_sender:
title: "Mensaje privado enviado"
description: "Enviado al remitente de un mensaje privado."
email_verification:
title: "Verificación por email"
description: "Enviado al nuevo usuario registrado para verificar su cuenta."
user_invite:
title: "Invitación de usuarios"
description: "Enviado a la persona que ha sido invitada a registrar una cuenta."
edit_info: "Puedes editar este email en"
message_title: "Título del mensaje"
message_body: "Este es un ejemplo de contenido de un mensaje."
alert:
no_investments: "No se ha creado ningún proyecto de gasto. Se necesita algún ejemplo para poder previsualizar el email."
no_comments: "No se ha creado ningún comentario. Se necesita algún ejemplo para poder previsualizar el email."
no_replies: "No se ha creado ninguna respuesta. Se necesita algún ejemplo para poder previsualizar el email."
emails_download:
index:
title: Descarga de emails

27
lib/reply_email.rb Normal file
View File

@@ -0,0 +1,27 @@
class ReplyEmail
attr_reader :reply
def initialize(reply)
@reply = reply
end
def commentable
reply.commentable
end
def recipient
reply.parent.author
end
def to
recipient.email
end
def subject
I18n.t("mailers.reply.subject")
end
def can_be_sent?
commentable.present? && recipient.present?
end
end

View File

@@ -2,22 +2,82 @@ require "rails_helper"
feature "System Emails" do
let(:admin) { create(:administrator) }
background do
admin = create(:administrator)
login_as(admin.user)
end
context "Index" do
scenario "Lists all system emails with correct actions" do
visit admin_system_emails_path
within("#proposal_notification_digest") do
expect(page).to have_link("View")
let(:system_emails_with_preview) { %w[proposal_notification_digest] }
let(:system_emails) do
%w[proposal_notification_digest budget_investment_created budget_investment_selected
budget_investment_unfeasible budget_investment_unselected comment reply
direct_message_for_receiver direct_message_for_sender email_verification user_invite]
end
context "System emails" do
scenario "have 'View' button" do
visit admin_system_emails_path
system_emails.each do |email_id|
within("##{email_id}") do
expect(page).to have_link("View", href: admin_system_email_view_path(email_id))
end
end
end
end
context "System emails with preview" do
scenario "have 'Preview Pending' and 'Send pending' buttons" do
visit admin_system_emails_path
system_emails_with_preview.each do |email_id|
within("##{email_id}") do
expect(page).to have_link("Preview Pending",
href: admin_system_email_preview_pending_path(email_id))
expect(page).to have_link("Send pending",
href: admin_system_email_send_pending_path(email_id))
expect(page).not_to have_content "You can edit this email in"
expect(page).not_to have_content "app/views/mailer/#{email_id}.html.erb"
end
end
end
end
context "System emails with info" do
scenario "have information about how to edit the email templates" do
visit admin_system_emails_path
system_emails_with_info = system_emails - system_emails_with_preview
system_emails_with_info.each do |email_id|
within("##{email_id}") do
expect(page).to have_content "You can edit this email in"
expect(page).to have_content "app/views/mailer/#{email_id}.html.erb"
expect(page).not_to have_link "Preview Pending"
expect(page).not_to have_link "Send pending"
end
end
end
end
end
context "View" do
let(:user) { create(:user, :level_two, username: "John Doe") }
let(:budget) { create(:budget, name: "Budget for 2019") }
let(:group) { create(:budget_group, budget: budget) }
let(:heading) { create(:budget_heading, group: group) }
scenario "#proposal_notification_digest" do
proposal_a = create(:proposal, title: "Proposal A")
proposal_b = create(:proposal, title: "Proposal B")
@@ -40,6 +100,151 @@ feature "System Emails" do
expect(page).to have_content("Proposal A Notification Body")
expect(page).to have_content("Proposal B Notification Body")
end
scenario "#budget_investment_created" do
investment = create(:budget_investment, title: "Cleaner city", heading: heading, author: user)
visit admin_system_email_view_path("budget_investment_created")
expect(page).to have_content "Thank you for creating an investment!"
expect(page).to have_content "John Doe"
expect(page).to have_content "Cleaner city"
expect(page).to have_content "Budget for 2019"
expect(page).to have_link "Participatory Budgets", href: budgets_url
share_url = budget_investment_url(budget, investment, anchor: "social-share")
expect(page).to have_link "Share your project", href: share_url
end
scenario "#budget_investment_selected" do
investment = create(:budget_investment, title: "Cleaner city", heading: heading, author: user)
visit admin_system_email_view_path("budget_investment_selected")
expect(page).to have_content "Your investment project '#{investment.code}' has been selected"
expect(page).to have_content "Start to get votes, share your investment project"
share_url = budget_investment_url(budget, investment, anchor: "social-share")
expect(page).to have_link "Share your investment project", href: share_url
end
scenario "#budget_investment_unfeasible" do
investment = create(:budget_investment, title: "Cleaner city", heading: heading, author: user)
visit admin_system_email_view_path("budget_investment_unfeasible")
expect(page).to have_content "Your investment project '#{investment.code}' "
expect(page).to have_content "has been marked as unfeasible"
end
scenario "#budget_investment_unselected" do
investment = create(:budget_investment, title: "Cleaner city", heading: heading, author: user)
visit admin_system_email_view_path("budget_investment_unselected")
expect(page).to have_content "Your investment project '#{investment.code}' "
expect(page).to have_content "has not been selected"
expect(page).to have_content "Thank you again for participating."
end
scenario "#comment" do
debate = create(:debate, title: "Let's do...", author: user)
commenter = create(:user)
comment = create(:comment, commentable: debate, author: commenter)
visit admin_system_email_view_path("comment")
expect(page).to have_content "Someone has commented on your Debate"
expect(page).to have_content "Hi John Doe,"
expect(page).to have_content "There is a new comment from #{commenter.name}"
expect(page).to have_content comment.body
expect(page).to have_link "Let's do...", href: debate_url(debate)
end
scenario "#reply" do
debate = create(:debate, title: "Let's do...", author: user)
comment = create(:comment, commentable: debate, author: user)
replier = create(:user)
reply = create(:comment, commentable: debate, parent: comment, author: replier)
visit admin_system_email_view_path("reply")
expect(page).to have_content "Someone has responded to your comment"
expect(page).to have_content "Hi John Doe,"
expect(page).to have_content "There is a new response from #{replier.name}"
expect(page).to have_content reply.body
expect(page).to have_link "Let's do...", href: comment_url(reply)
end
scenario "#direct_message_for_receiver" do
visit admin_system_email_view_path("direct_message_for_receiver")
expect(page).to have_content "You have received a new private message"
expect(page).to have_content "Message's Title"
expect(page).to have_content "This is a sample of message's content."
expect(page).to have_link "Reply to #{admin.user.name}", href: user_url(admin.user)
end
scenario "#direct_message_for_sender" do
visit admin_system_email_view_path("direct_message_for_sender")
expect(page).to have_content "You have sent a new private message to #{admin.user.name}"
expect(page).to have_content "Message's Title"
expect(page).to have_content "This is a sample of message's content."
end
scenario "#email_verification" do
create(:user, confirmed_at: nil, email_verification_token: "abc")
visit admin_system_email_view_path("email_verification")
expect(page).to have_content "Confirm your account using the following link"
expect(page).to have_link "this link", href: email_url(email_verification_token: "abc")
end
scenario "#user_invite" do
visit admin_system_email_view_path("user_invite")
expect(page).to have_content "Invitation to CONSUL"
expect(page).to have_content "Thank you for applying to join CONSUL!"
registration_url = new_user_registration_url(track_id: 172943750183759812)
expect(page).to have_link "Complete registration"
end
scenario "show flash message if there is no sample data to render the email" do
visit admin_system_email_view_path("budget_investment_created")
expect(page).to have_content "There aren't any budget investment created."
expect(page).to have_content "Some example data is needed in order to preview the email."
visit admin_system_email_view_path("budget_investment_selected")
expect(page).to have_content "There aren't any budget investment created."
expect(page).to have_content "Some example data is needed in order to preview the email."
visit admin_system_email_view_path("budget_investment_unfeasible")
expect(page).to have_content "There aren't any budget investment created."
expect(page).to have_content "Some example data is needed in order to preview the email."
visit admin_system_email_view_path("budget_investment_unselected")
expect(page).to have_content "There aren't any budget investment created."
expect(page).to have_content "Some example data is needed in order to preview the email."
visit admin_system_email_view_path("comment")
expect(page).to have_content "There aren't any comments created."
expect(page).to have_content "Some example data is needed in order to preview the email."
visit admin_system_email_view_path("reply")
expect(page).to have_content "There aren't any replies created."
expect(page).to have_content "Some example data is needed in order to preview the email."
end
end
context "Preview Pending" do

View File

@@ -0,0 +1,57 @@
require "rails_helper"
describe ReplyEmail do
let(:author) { create(:user) }
let(:debate) { create(:debate, author: author) }
let(:commenter) { create(:user, email: "email@commenter.org") }
let(:comment) { create(:comment, commentable: debate, user: commenter) }
let(:replier) { create(:user) }
let(:reply) { create(:comment, commentable: debate, parent: comment, user: replier) }
let(:reply_email) { ReplyEmail.new(reply) }
describe "#commentable" do
it "returns the commentable object that contains the replied comment" do
expect(reply_email.commentable).to eq debate
end
end
describe "#recipient" do
it "returns the author of the replied comment" do
expect(reply_email.recipient).to eq commenter
end
end
describe "#to" do
it "returns the author's email of the replied comment" do
expect(reply_email.to).to eq "email@commenter.org"
end
end
describe "#subject" do
it "returns the translation for a reply email subject" do
expect(reply_email.subject).to eq "Someone has responded to your comment"
end
end
describe "#can_be_sent?" do
it "returns true if comment and recipient exist" do
expect(reply_email.can_be_sent?).to be true
end
it "returns false if the comment doesn't exist" do
reply.update(commentable: nil)
expect(reply_email.can_be_sent?).to be false
end
it "returns false if the recipient doesn't exist" do
reply.parent.author.really_destroy!
expect(reply_email.can_be_sent?).to be false
end
end
end