diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index b7e6946c7..17ea18b9d 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -410,6 +410,10 @@ $sidebar-active: #f4fcd0; } } +code { + word-break: break-all; +} + // 02. Sidebar // ----------- diff --git a/app/controllers/admin/system_emails_controller.rb b/app/controllers/admin/system_emails_controller.rb index d079c171b..689425569 100644 --- a/app/controllers/admin/system_emails_controller.rb +++ b/app/controllers/admin/system_emails_controller.rb @@ -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 diff --git a/app/mailers/mailer.rb b/app/mailers/mailer.rb index 2cdb494c3..7013130cd 100644 --- a/app/mailers/mailer.rb +++ b/app/mailers/mailer.rb @@ -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 diff --git a/app/views/admin/system_emails/index.html.erb b/app/views/admin/system_emails/index.html.erb index 116cf2c76..be20a5502 100644 --- a/app/views/admin/system_emails/index.html.erb +++ b/app/views/admin/system_emails/index.html.erb @@ -24,19 +24,27 @@ class: "button hollow expanded" %> <% end %> - <% if system_email_actions.include?('preview_pending') %> + <% if system_email_actions.include?("preview_pending") %>
<%= link_to t("admin.system_emails.preview_pending.action"), admin_system_email_preview_pending_path(system_email_title), class: "button expanded" %>
+
+ <%= 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 %> +
+ <% end %> + <% if system_email_actions.include?("edit_info") %> +
+

+ <%= t("admin.system_emails.edit_info") %>
+ <%= "app/views/mailer/#{system_email_title}.html.erb" %> +

+
<% end %> -
- <%= 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 %> -
<% end %> diff --git a/app/views/admin/system_emails/view.html.erb b/app/views/admin/system_emails/view.html.erb index 9aaeb5226..685a89a4e 100644 --- a/app/views/admin/system_emails/view.html.erb +++ b/app/views/admin/system_emails/view.html.erb @@ -11,7 +11,7 @@
<%= t("admin.newsletters.show.subject") %>
- <%= @subject %> + <%= @subject || @email.subject %>
diff --git a/app/views/mailer/reply.html.erb b/app/views/mailer/reply.html.erb index d2b6f6858..02478e3ad 100644 --- a/app/views/mailer/reply.html.erb +++ b/app/views/mailer/reply.html.erb @@ -5,16 +5,16 @@

- <%= t("mailers.reply.hi") %> <%= @recipient.name %>, + <%= t("mailers.reply.hi") %> <%= @email.recipient.name %>,

- <%= 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;" %>

-

- <%= text_with_links @reply.body %> -

+
+ <%= simple_format text_with_links(@email.reply.body), {}, sanitize: false %> +

<%= t("mailers.config.manage_email_subscriptions") %> <%= link_to t("account.show.title"), account_url, style: "color: #2895F1; text-decoration:none;" %> diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index cc2293069..d47e08658 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -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 diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 4186560a4..b4ae38989 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -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 diff --git a/lib/reply_email.rb b/lib/reply_email.rb new file mode 100644 index 000000000..563368348 --- /dev/null +++ b/lib/reply_email.rb @@ -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 diff --git a/spec/features/admin/system_emails_spec.rb b/spec/features/admin/system_emails_spec.rb index fb0eb374c..831e560f7 100644 --- a/spec/features/admin/system_emails_spec.rb +++ b/spec/features/admin/system_emails_spec.rb @@ -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 diff --git a/spec/lib/reply_email_spec.rb b/spec/lib/reply_email_spec.rb new file mode 100644 index 000000000..87819e3d1 --- /dev/null +++ b/spec/lib/reply_email_spec.rb @@ -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