Commit iniziale di eminuxCRM: CRM Rails con pipeline, campagne email e Docker.
Include autenticazione, progetti isolati, mail marketing HTML con SMTP, test A/B e editor WYSIWYG. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
class MailingsController < ApplicationController
|
||||
before_action :require_current_project!
|
||||
before_action :set_mailing, only: %i[show edit update destroy queue test_send refresh_recipients update_recipients preview]
|
||||
before_action :load_form_collections, only: %i[new create edit update]
|
||||
|
||||
def index
|
||||
@page_title = "Email"
|
||||
@mailings = Mailing.for_project(current_project).includes(:mail_identity).recent
|
||||
@mail_identities_count = MailIdentity.active.count
|
||||
end
|
||||
|
||||
def new
|
||||
@page_title = "Nuovo invio"
|
||||
template = MailTemplate.for_project(current_project).order(:name).first
|
||||
identity = MailIdentity.active.ordered.first
|
||||
@mailing = Mailing.new(
|
||||
project: current_project,
|
||||
mail_template: template,
|
||||
mail_identity: identity,
|
||||
audience: "to_send",
|
||||
interval_seconds: 0,
|
||||
ab_assignment: "from_record",
|
||||
name: "Invio #{l(Time.zone.today)}",
|
||||
subject: template&.subject,
|
||||
body_html: template&.body_html
|
||||
)
|
||||
end
|
||||
|
||||
def create
|
||||
@mailing = Mailing.new(mailing_params)
|
||||
@mailing.project = current_project
|
||||
apply_template_if_needed
|
||||
if @mailing.save
|
||||
@mailing.rebuild_recipients!
|
||||
redirect_to @mailing, notice: "Bozza creata. Controlla i destinatari e poi invia."
|
||||
else
|
||||
@page_title = "Nuovo invio"
|
||||
render :new, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
@page_title = @mailing.name
|
||||
@recipients = @mailing.mailing_recipients.includes(:organization, :contact).ordered
|
||||
@preview_recipient = preview_recipient
|
||||
@preview_variant = preview_variant
|
||||
context = @preview_recipient&.merge_context || { project: current_project, ab_variant: @preview_variant }
|
||||
@preview_html = MailMerge.render(@mailing.body_for(@preview_variant), **context)
|
||||
@preview_subject = MailMerge.render(@mailing.subject_for(@preview_variant), **context)
|
||||
end
|
||||
|
||||
def edit
|
||||
unless @mailing.editable?
|
||||
redirect_to @mailing, alert: "Questo invio non è più modificabile."
|
||||
return
|
||||
end
|
||||
@page_title = "Modifica invio"
|
||||
end
|
||||
|
||||
def update
|
||||
unless @mailing.editable?
|
||||
redirect_to @mailing, alert: "Questo invio non è più modificabile."
|
||||
return
|
||||
end
|
||||
|
||||
@mailing.assign_attributes(mailing_params)
|
||||
apply_template_if_needed
|
||||
if @mailing.save
|
||||
rebuild_recipients_if_needed
|
||||
redirect_to @mailing, notice: "Invio aggiornato."
|
||||
else
|
||||
@page_title = "Modifica invio"
|
||||
render :edit, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
unless @mailing.draft?
|
||||
redirect_to mailings_path, alert: "Puoi eliminare solo le bozze."
|
||||
return
|
||||
end
|
||||
|
||||
@mailing.destroy!
|
||||
redirect_to mailings_path, notice: "Invio eliminato."
|
||||
end
|
||||
|
||||
def refresh_recipients
|
||||
unless @mailing.editable?
|
||||
redirect_to @mailing, alert: "Destinatari bloccati: l'invio è già partito."
|
||||
return
|
||||
end
|
||||
|
||||
@mailing.rebuild_recipients!
|
||||
redirect_to @mailing, notice: "Lista destinatari aggiornata."
|
||||
end
|
||||
|
||||
def update_recipients
|
||||
unless @mailing.editable?
|
||||
redirect_to @mailing, alert: "Non puoi più cambiare i destinatari."
|
||||
return
|
||||
end
|
||||
|
||||
selected = Array(params[:pending_ids]).map(&:to_i)
|
||||
@mailing.mailing_recipients.where.not(status: %w[sent queued]).find_each do |recipient|
|
||||
if selected.include?(recipient.id) && recipient.email_ok?
|
||||
recipient.update!(status: "pending", skip_reason: nil)
|
||||
else
|
||||
reason = recipient.email_ok? ? "escluso dal check" : (recipient.skip_reason.presence || "email non valida")
|
||||
recipient.update!(status: "skipped", skip_reason: reason)
|
||||
end
|
||||
end
|
||||
redirect_to @mailing, notice: "Selezione destinatari salvata."
|
||||
end
|
||||
|
||||
def queue
|
||||
unless @mailing.draft? || @mailing.sending?
|
||||
redirect_to @mailing, alert: "Questo invio è già stato concluso."
|
||||
return
|
||||
end
|
||||
|
||||
@mailing.queue_send!
|
||||
redirect_to @mailing, notice: "Invio avviato. Le email partono in background#{@mailing.interval_seconds.positive? ? " con pausa di #{@mailing.interval_seconds}s" : ""}."
|
||||
rescue StandardError => e
|
||||
redirect_to @mailing, alert: e.message
|
||||
end
|
||||
|
||||
def test_send
|
||||
recipient = preview_recipient
|
||||
unless recipient&.email_ok?
|
||||
redirect_to @mailing, alert: "Nessun destinatario valido da usare come dati di prova."
|
||||
return
|
||||
end
|
||||
|
||||
variant = preview_variant
|
||||
html = MailMerge.render(@mailing.body_for(variant), **recipient.merge_context.merge(ab_variant: variant))
|
||||
subject = "[TEST#{@mailing.ab_test? ? " #{variant}" : ""}] #{MailMerge.render(@mailing.subject_for(variant), **recipient.merge_context.merge(ab_variant: variant))}"
|
||||
test_recipient = recipient.dup
|
||||
test_recipient.email = current_user.email
|
||||
CampaignMailer.outreach(test_recipient, html: html, subject: subject).deliver_now
|
||||
redirect_to mailing_path(@mailing, recipient_id: recipient.id, variant: variant), notice: "Email di prova (#{@mailing.ab_test? ? "variante #{variant}" : "unica"}) inviata a #{current_user.email}."
|
||||
rescue StandardError => e
|
||||
redirect_to @mailing, alert: "Invio di prova non riuscito: #{e.message}"
|
||||
end
|
||||
|
||||
def preview
|
||||
redirect_to mailing_path(@mailing, recipient_id: params[:recipient_id])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_mailing
|
||||
@mailing = Mailing.for_project(current_project).find(params[:id])
|
||||
end
|
||||
|
||||
def load_form_collections
|
||||
@mail_identities = MailIdentity.active.ordered
|
||||
@mail_templates = MailTemplate.for_project(current_project)
|
||||
end
|
||||
|
||||
def mailing_params
|
||||
params.require(:mailing).permit(
|
||||
:name, :mail_identity_id, :mail_template_id, :mail_template_b_id, :audience,
|
||||
:subject, :body_html, :subject_b, :body_html_b, :ab_test, :ab_assignment,
|
||||
:interval_seconds, files: []
|
||||
)
|
||||
end
|
||||
|
||||
def apply_template_if_needed
|
||||
copy_template(params.dig(:mailing, :mail_template_id), :subject, :body_html, params[:use_template_content])
|
||||
copy_template(params.dig(:mailing, :mail_template_b_id), :subject_b, :body_html_b, params[:use_template_b_content])
|
||||
end
|
||||
|
||||
def copy_template(template_id, subject_attr, body_attr, flag)
|
||||
return if template_id.blank?
|
||||
return unless ActiveModel::Type::Boolean.new.cast(flag)
|
||||
|
||||
template = MailTemplate.for_project(current_project).find_by(id: template_id)
|
||||
return unless template
|
||||
|
||||
@mailing.public_send("#{subject_attr}=", template.subject)
|
||||
@mailing.public_send("#{body_attr}=", template.body_html)
|
||||
end
|
||||
|
||||
def rebuild_recipients_if_needed
|
||||
return unless @mailing.editable?
|
||||
return unless @mailing.saved_change_to_audience? || @mailing.saved_change_to_ab_test? || @mailing.saved_change_to_ab_assignment?
|
||||
|
||||
@mailing.rebuild_recipients!
|
||||
end
|
||||
|
||||
def preview_recipient
|
||||
if params[:recipient_id].present?
|
||||
@mailing.mailing_recipients.find_by(id: params[:recipient_id])
|
||||
else
|
||||
@mailing.mailing_recipients.pending.ordered.first || @mailing.mailing_recipients.ordered.first
|
||||
end
|
||||
end
|
||||
|
||||
def preview_variant
|
||||
requested = params[:variant].to_s.upcase
|
||||
return requested if requested.in?(%w[A B])
|
||||
return "A" unless @mailing.ab_test?
|
||||
|
||||
preview_recipient&.ab_variant.presence_in(%w[A B]) || "A"
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user