mirror of
https://github.com/plausible/analytics.git
synced 2024-12-23 01:22:15 +03:00
Add and remove recipients for email reports (#28)
* Add and remove recipients for email reports * Remove unused google_settings controller action * Background job sends email reports to multiple recipients * Add a way to unsubscribe for recipients who cannot log in * Fix view on plausible link * Include bounce rate in email report
This commit is contained in:
parent
66ef40c91d
commit
c96f364ad8
@ -2,89 +2,98 @@ defmodule Mix.Tasks.SendEmailReports do
|
||||
use Mix.Task
|
||||
use Plausible.Repo
|
||||
require Logger
|
||||
alias Plausible.Stats
|
||||
|
||||
def run(args) do
|
||||
def run(_args) do
|
||||
Application.ensure_all_started(:plausible)
|
||||
execute(args)
|
||||
execute(Timex.now())
|
||||
end
|
||||
|
||||
@doc"""
|
||||
The email report should be sent on Monday at 9am according to the timezone
|
||||
of the site. This job runs every hour to be able to send it with hourly precision.
|
||||
"""
|
||||
def execute(_args \\ []) do
|
||||
send_weekly_emails()
|
||||
send_monthly_emails()
|
||||
def execute(job_start) do
|
||||
send_weekly_emails(job_start)
|
||||
send_monthly_emails(job_start)
|
||||
end
|
||||
|
||||
defp send_weekly_emails() do
|
||||
defp send_weekly_emails(job_start) do
|
||||
sites = Repo.all(
|
||||
from s in Plausible.Site,
|
||||
join: wr in Plausible.Site.WeeklyReport, on: wr.site_id == s.id,
|
||||
left_join: se in "sent_weekly_reports", on: se.site_id == s.id and se.year == fragment("EXTRACT(isoyear from (now() at time zone ?))", s.timezone) and se.week == fragment("EXTRACT(week from (now() at time zone ?))", s.timezone),
|
||||
left_join: se in "sent_weekly_reports", on: se.site_id == s.id and se.year == fragment("EXTRACT(isoyear from (? at time zone ?))", ^job_start, s.timezone) and se.week == fragment("EXTRACT(week from (? at time zone ?))", ^job_start, s.timezone),
|
||||
where: is_nil(se), # We haven't sent a report for this site on this week
|
||||
where: fragment("EXTRACT(dow from (now() at time zone ?))", s.timezone) == 1, # It's monday in the local timezone
|
||||
where: fragment("EXTRACT(hour from (now() at time zone ?))", s.timezone) >= 9, # It's after 9am
|
||||
where: fragment("EXTRACT(dow from (? at time zone ?))", ^job_start, s.timezone) == 1, # It's monday in the local timezone
|
||||
where: fragment("EXTRACT(hour from (? at time zone ?))", ^job_start, s.timezone) >= 9, # It's after 9am
|
||||
preload: [weekly_report: wr]
|
||||
)
|
||||
|
||||
for site <- sites do
|
||||
email = site.weekly_report.email
|
||||
query = Plausible.Stats.Query.from(site.timezone, %{"period" => "7d"})
|
||||
query = Stats.Query.from(site.timezone, %{"period" => "7d"})
|
||||
|
||||
IO.puts("Sending weekly report for #{site.domain} to #{email}")
|
||||
for email <- site.weekly_report.recipients do
|
||||
Logger.info("Sending weekly report for #{site.domain} to #{email}")
|
||||
unsubscribe_link = PlausibleWeb.Endpoint.url() <> "/sites/#{site.domain}/weekly-report/unsubscribe?email=#{email}"
|
||||
send_report(email, site, "Weekly", unsubscribe_link, query)
|
||||
end
|
||||
|
||||
send_report(email, site, query)
|
||||
weekly_report_sent(site)
|
||||
weekly_report_sent(site, job_start)
|
||||
end
|
||||
end
|
||||
|
||||
defp send_monthly_emails() do
|
||||
defp send_monthly_emails(job_start) do
|
||||
sites = Repo.all(
|
||||
from s in Plausible.Site,
|
||||
join: mr in Plausible.Site.MonthlyReport, on: mr.site_id == s.id,
|
||||
left_join: se in "sent_monthly_reports", on: se.site_id == s.id and se.year == fragment("EXTRACT(year from (now() at time zone ?))", s.timezone) and se.month == fragment("EXTRACT(month from (now() at time zone ?))", s.timezone),
|
||||
left_join: se in "sent_monthly_reports", on: se.site_id == s.id and se.year == fragment("EXTRACT(year from (? at time zone ?))", ^job_start, s.timezone) and se.month == fragment("EXTRACT(month from (? at time zone ?))", ^job_start, s.timezone),
|
||||
where: is_nil(se), # We haven't sent a report for this site this month
|
||||
where: fragment("EXTRACT(day from (now() at time zone ?))", s.timezone) == 1, # It's the 1st of the month in the local timezone
|
||||
where: fragment("EXTRACT(hour from (now() at time zone ?))", s.timezone) >= 9, # It's after 9am
|
||||
where: fragment("EXTRACT(day from (? at time zone ?))", ^job_start, s.timezone) == 1, # It's the 1st of the month in the local timezone
|
||||
where: fragment("EXTRACT(hour from (? at time zone ?))", ^job_start, s.timezone) >= 9, # It's after 9am
|
||||
preload: [monthly_report: mr]
|
||||
)
|
||||
|
||||
for site <- sites do
|
||||
email = site.monthly_report.email
|
||||
last_month = Timex.now(site.timezone) |> Timex.shift(months: -1) |> Timex.beginning_of_month |> Timex.format!("{ISOdate}")
|
||||
query = Plausible.Stats.Query.from(site.timezone, %{"period" => "month", "date" => last_month})
|
||||
last_month = job_start |> Timex.Timezone.convert(site.timezone) |> Timex.shift(months: -1) |> Timex.beginning_of_month
|
||||
query = Stats.Query.from(site.timezone, %{"period" => "month", "date" => Timex.format!(last_month, "{ISOdate}")})
|
||||
|
||||
IO.puts("Sending monthly report for #{site.domain} to #{email}")
|
||||
for email <- site.monthly_report.recipients do
|
||||
Logger.info("Sending monthly report for #{site.domain} to #{email}")
|
||||
unsubscribe_link = PlausibleWeb.Endpoint.url() <> "/sites/#{site.domain}/monthly-report/unsubscribe?email=#{email}"
|
||||
send_report(email, site, Timex.format!(last_month, "{Mfull}"), unsubscribe_link, query)
|
||||
end
|
||||
|
||||
send_report(email, site, query)
|
||||
monthly_report_sent(site)
|
||||
monthly_report_sent(site, job_start)
|
||||
end
|
||||
end
|
||||
|
||||
defp send_report(email, site, query) do
|
||||
{pageviews, unique_visitors} = Plausible.Stats.pageviews_and_visitors(site, query)
|
||||
{change_pageviews, change_visitors} = Plausible.Stats.compare_pageviews_and_visitors(site, query, {pageviews, unique_visitors})
|
||||
referrers = Plausible.Stats.top_referrers(site, query)
|
||||
pages = Plausible.Stats.top_pages(site, query)
|
||||
settings_link = PlausibleWeb.Endpoint.url() <> "/#{site.domain}/settings#email-reports"
|
||||
view_link = PlausibleWeb.Endpoint.url() <> "/#{site.domain}?period=7d"
|
||||
defp send_report(email, site, name, unsubscribe_link, query) do
|
||||
{pageviews, unique_visitors} = Stats.pageviews_and_visitors(site, query)
|
||||
{change_pageviews, change_visitors} = Stats.compare_pageviews_and_visitors(site, query, {pageviews, unique_visitors})
|
||||
bounce_rate = Stats.bounce_rate(site, query)
|
||||
prev_bounce_rate = Stats.bounce_rate(site, Stats.Query.shift_back(query))
|
||||
change_bounce_rate = if prev_bounce_rate > 0, do: bounce_rate - prev_bounce_rate
|
||||
referrers = Stats.top_referrers(site, query)
|
||||
pages = Stats.top_pages(site, query)
|
||||
|
||||
PlausibleWeb.Email.weekly_report(email, site,
|
||||
unique_visitors: unique_visitors,
|
||||
change_visitors: change_visitors,
|
||||
pageviews: pageviews,
|
||||
change_pageviews: change_pageviews,
|
||||
bounce_rate: bounce_rate,
|
||||
change_bounce_rate: change_bounce_rate,
|
||||
referrers: referrers,
|
||||
settings_link: settings_link,
|
||||
view_link: view_link,
|
||||
unsubscribe_link: unsubscribe_link,
|
||||
view_link: view_link(site, query),
|
||||
pages: pages,
|
||||
query: query
|
||||
query: query,
|
||||
name: name
|
||||
) |> Plausible.Mailer.deliver_now()
|
||||
end
|
||||
|
||||
defp weekly_report_sent(site) do
|
||||
{year, week} = Timex.now(site.timezone) |> DateTime.to_date |> Timex.iso_week
|
||||
defp weekly_report_sent(site, time) do
|
||||
{year, week} = time |> DateTime.to_date |> Timex.iso_week
|
||||
|
||||
Repo.insert_all("sent_weekly_reports", [%{
|
||||
site_id: site.id,
|
||||
@ -94,8 +103,8 @@ defmodule Mix.Tasks.SendEmailReports do
|
||||
}])
|
||||
end
|
||||
|
||||
defp monthly_report_sent(site) do
|
||||
date = Timex.now(site.timezone) |> DateTime.to_date
|
||||
defp monthly_report_sent(site, time) do
|
||||
date = DateTime.to_date(time)
|
||||
|
||||
Repo.insert_all("sent_monthly_reports", [%{
|
||||
site_id: site.id,
|
||||
@ -104,4 +113,13 @@ defmodule Mix.Tasks.SendEmailReports do
|
||||
timestamp: Timex.now()
|
||||
}])
|
||||
end
|
||||
|
||||
defp view_link(site, %Plausible.Stats.Query{period: "7d"}) do
|
||||
PlausibleWeb.Endpoint.url() <> "/#{site.domain}?period=7d"
|
||||
end
|
||||
|
||||
defp view_link(site, %Plausible.Stats.Query{period: "month", date_range: range}) do
|
||||
month = Timex.format!(range.first, "{ISOdate}")
|
||||
PlausibleWeb.Endpoint.url() <> "/#{site.domain}?period=month&date=#{month}"
|
||||
end
|
||||
end
|
||||
|
@ -1,10 +1,9 @@
|
||||
defmodule Plausible.Site.MonthlyReport do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
@mail_regex ~r/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/
|
||||
|
||||
schema "monthly_reports" do
|
||||
field :email, :string
|
||||
field :recipients, {:array, :string}
|
||||
belongs_to :site, Plausible.Site
|
||||
|
||||
timestamps()
|
||||
@ -12,9 +11,18 @@ defmodule Plausible.Site.MonthlyReport do
|
||||
|
||||
def changeset(settings, attrs \\ %{}) do
|
||||
settings
|
||||
|> cast(attrs, [:site_id, :email])
|
||||
|> validate_required([:site_id, :email])
|
||||
|> validate_format(:email, @mail_regex)
|
||||
|> cast(attrs, [:site_id, :recipients])
|
||||
|> validate_required([:site_id, :recipients])
|
||||
|> unique_constraint(:site)
|
||||
end
|
||||
|
||||
def add_recipient(report, recipient) do
|
||||
report
|
||||
|> change(recipients: report.recipients ++ [recipient])
|
||||
end
|
||||
|
||||
def remove_recipient(report, recipient) do
|
||||
report
|
||||
|> change(recipients: List.delete(report.recipients, recipient))
|
||||
end
|
||||
end
|
||||
|
@ -1,10 +1,9 @@
|
||||
defmodule Plausible.Site.WeeklyReport do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
@mail_regex ~r/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/
|
||||
|
||||
schema "weekly_reports" do
|
||||
field :email, :string
|
||||
field :recipients, {:array, :string}
|
||||
belongs_to :site, Plausible.Site
|
||||
|
||||
timestamps()
|
||||
@ -12,9 +11,18 @@ defmodule Plausible.Site.WeeklyReport do
|
||||
|
||||
def changeset(settings, attrs \\ %{}) do
|
||||
settings
|
||||
|> cast(attrs, [:site_id, :email])
|
||||
|> validate_required([:site_id, :email])
|
||||
|> validate_format(:email, @mail_regex)
|
||||
|> cast(attrs, [:site_id, :recipients])
|
||||
|> validate_required([:site_id, :recipients])
|
||||
|> unique_constraint(:site)
|
||||
end
|
||||
|
||||
def add_recipient(report, recipient) do
|
||||
report
|
||||
|> change(recipients: report.recipients ++ [recipient])
|
||||
end
|
||||
|
||||
def remove_recipient(report, recipient) do
|
||||
report
|
||||
|> change(recipients: List.delete(report.recipients, recipient))
|
||||
end
|
||||
end
|
||||
|
@ -82,17 +82,15 @@ defmodule PlausibleWeb.SiteController do
|
||||
end
|
||||
|
||||
weekly_report = Repo.get_by(Plausible.Site.WeeklyReport, site_id: site.id)
|
||||
weekly_report_changeset = weekly_report && Plausible.Site.WeeklyReport.changeset(weekly_report, %{})
|
||||
monthly_report = Repo.get_by(Plausible.Site.MonthlyReport, site_id: site.id)
|
||||
monthly_report_changeset = monthly_report && Plausible.Site.WeeklyReport.changeset(monthly_report, %{})
|
||||
goals = Goals.for_site(site.domain)
|
||||
|
||||
conn
|
||||
|> assign(:skip_plausible_tracking, true)
|
||||
|> render("settings.html",
|
||||
site: site,
|
||||
weekly_report_changeset: weekly_report_changeset,
|
||||
monthly_report_changeset: monthly_report_changeset,
|
||||
weekly_report: weekly_report,
|
||||
monthly_report: monthly_report,
|
||||
search_console_domains: search_console_domains,
|
||||
goals: goals,
|
||||
changeset: Plausible.Site.changeset(site, %{})
|
||||
@ -172,13 +170,13 @@ defmodule PlausibleWeb.SiteController do
|
||||
|
||||
Plausible.Site.WeeklyReport.changeset(%Plausible.Site.WeeklyReport{}, %{
|
||||
site_id: site.id,
|
||||
email: conn.assigns[:current_user].email
|
||||
recipients: [conn.assigns[:current_user].email]
|
||||
})
|
||||
|> Repo.insert!
|
||||
|
||||
conn
|
||||
|> put_flash(:success, "Success! You will receive an email report every Monday going forward")
|
||||
|> redirect(to: "/" <> site.domain <> "/settings")
|
||||
|> redirect(to: "/" <> site.domain <> "/settings#email-reports")
|
||||
end
|
||||
|
||||
def disable_weekly_report(conn, %{"website" => website}) do
|
||||
@ -187,18 +185,31 @@ defmodule PlausibleWeb.SiteController do
|
||||
|
||||
conn
|
||||
|> put_flash(:success, "Success! You will not receive weekly email reports going forward")
|
||||
|> redirect(to: "/" <> site.domain <> "/settings")
|
||||
|> redirect(to: "/" <> site.domain <> "/settings#email-reports")
|
||||
end
|
||||
|
||||
def update_weekly_settings(conn, %{"website" => website, "weekly_report" => weekly_report}) do
|
||||
def add_weekly_report_recipient(conn, %{"website" => website, "recipient" => recipient}) do
|
||||
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
|
||||
|
||||
Repo.get_by(Plausible.Site.WeeklyReport, site_id: site.id)
|
||||
|> Plausible.Site.WeeklyReport.changeset(weekly_report)
|
||||
|> Plausible.Site.WeeklyReport.add_recipient(recipient)
|
||||
|> Repo.update!
|
||||
|
||||
conn
|
||||
|> put_flash(:success, "Email address saved succesfully")
|
||||
|> redirect(to: "/#{site.domain}/settings")
|
||||
|> put_flash(:success, "Succesfully added #{recipient} as a recipient for the weekly report")
|
||||
|> redirect(to: "/#{site.domain}/settings#email-reports")
|
||||
end
|
||||
|
||||
def remove_weekly_report_recipient(conn, %{"website" => website, "recipient" => recipient}) do
|
||||
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
|
||||
|
||||
Repo.get_by(Plausible.Site.WeeklyReport, site_id: site.id)
|
||||
|> Plausible.Site.WeeklyReport.remove_recipient(recipient)
|
||||
|> Repo.update!
|
||||
|
||||
conn
|
||||
|> put_flash(:success, "Succesfully removed #{recipient} as a recipient for the weekly report")
|
||||
|> redirect(to: "/#{site.domain}/settings#email-reports")
|
||||
end
|
||||
|
||||
def enable_monthly_report(conn, %{"website" => website}) do
|
||||
@ -206,13 +217,13 @@ defmodule PlausibleWeb.SiteController do
|
||||
|
||||
Plausible.Site.MonthlyReport.changeset(%Plausible.Site.MonthlyReport{}, %{
|
||||
site_id: site.id,
|
||||
email: conn.assigns[:current_user].email
|
||||
recipients: [conn.assigns[:current_user].email]
|
||||
})
|
||||
|> Repo.insert!
|
||||
|
||||
conn
|
||||
|> put_flash(:success, "Success! You will receive an email report every month going forward")
|
||||
|> redirect(to: "/" <> site.domain <> "/settings")
|
||||
|> redirect(to: "/" <> site.domain <> "/settings#email-reports")
|
||||
end
|
||||
|
||||
def disable_monthly_report(conn, %{"website" => website}) do
|
||||
@ -221,32 +232,31 @@ defmodule PlausibleWeb.SiteController do
|
||||
|
||||
conn
|
||||
|> put_flash(:success, "Success! You will not receive monthly email reports going forward")
|
||||
|> redirect(to: "/" <> site.domain <> "/settings")
|
||||
|> redirect(to: "/" <> site.domain <> "/settings#email-reports")
|
||||
end
|
||||
|
||||
def update_monthly_settings(conn, %{"website" => website, "monthly_report" => monthly_report}) do
|
||||
def add_monthly_report_recipient(conn, %{"website" => website, "recipient" => recipient}) do
|
||||
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
|
||||
|
||||
Repo.get_by(Plausible.Site.MonthlyReport, site_id: site.id)
|
||||
|> Plausible.Site.WeeklyReport.changeset(monthly_report)
|
||||
|> Plausible.Site.MonthlyReport.add_recipient(recipient)
|
||||
|> Repo.update!
|
||||
|
||||
conn
|
||||
|> put_flash(:success, "Email address saved succesfully")
|
||||
|> redirect(to: "/#{site.domain}/settings")
|
||||
|> put_flash(:success, "Succesfully added #{recipient} as a recipient for the monthly report")
|
||||
|> redirect(to: "/#{site.domain}/settings#email-reports")
|
||||
end
|
||||
|
||||
def google_settings(conn, %{"website" => website}) do
|
||||
def remove_monthly_report_recipient(conn, %{"website" => website, "recipient" => recipient}) do
|
||||
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
|
||||
|> Repo.preload(:google_auth)
|
||||
|
||||
verified_domains = Plausible.Google.Api.fetch_verified_properties(site.google_auth)
|
||||
Repo.get_by(Plausible.Site.MonthlyReport, site_id: site.id)
|
||||
|> Plausible.Site.MonthlyReport.remove_recipient(recipient)
|
||||
|> Repo.update!
|
||||
|
||||
render(conn,
|
||||
"google_settings.html",
|
||||
site: site,
|
||||
verified_domains: verified_domains,
|
||||
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
||||
)
|
||||
conn
|
||||
|> put_flash(:success, "Succesfully removed #{recipient} as a recipient for the monthly report")
|
||||
|> redirect(to: "/#{site.domain}/settings#email-reports")
|
||||
end
|
||||
|
||||
defp insert_site(user_id, params) do
|
||||
|
25
lib/plausible_web/controllers/unsubscribe_controller.ex
Normal file
25
lib/plausible_web/controllers/unsubscribe_controller.ex
Normal file
@ -0,0 +1,25 @@
|
||||
defmodule PlausibleWeb.UnsubscribeController do
|
||||
use PlausibleWeb, :controller
|
||||
use Plausible.Repo
|
||||
alias Plausible.Site.{WeeklyReport, MonthlyReport}
|
||||
|
||||
def weekly_report(conn, %{"website" => website, "email" => email}) do
|
||||
site = Repo.get_by(Plausible.Site, domain: website)
|
||||
|
||||
Repo.get_by(WeeklyReport, site_id: site.id)
|
||||
|> WeeklyReport.remove_recipient(email)
|
||||
|> Repo.update!
|
||||
|
||||
render(conn, "success.html", interval: "weekly", site: website, layout: {PlausibleWeb.LayoutView, "focus.html"})
|
||||
end
|
||||
|
||||
def monthly_report(conn, %{"website" => website, "email" => email}) do
|
||||
site = Repo.get_by(Plausible.Site, domain: website)
|
||||
|
||||
Repo.get_by(MonthlyReport, site_id: site.id)
|
||||
|> MonthlyReport.remove_recipient(email)
|
||||
|> Repo.update!
|
||||
|
||||
render(conn, "success.html", interval: "monthly", site: website, layout: {PlausibleWeb.LayoutView, "focus.html"})
|
||||
end
|
||||
end
|
@ -91,7 +91,7 @@ defmodule PlausibleWeb.Email do
|
||||
|> to(email)
|
||||
|> from("Plausible Insights <info@plausible.io>")
|
||||
|> tag("weekly-report")
|
||||
|> subject("Weekly report for #{site.domain}")
|
||||
|> subject("#{assigns[:name]} report for #{site.domain}")
|
||||
|> render("weekly_report.html", Keyword.put(assigns, :site, site))
|
||||
end
|
||||
end
|
||||
|
@ -102,10 +102,16 @@ defmodule PlausibleWeb.Router do
|
||||
post "/sites/:website/make-private", SiteController, :make_private
|
||||
post "/sites/:website/weekly-report/enable", SiteController, :enable_weekly_report
|
||||
post "/sites/:website/weekly-report/disable", SiteController, :disable_weekly_report
|
||||
put "/sites/:website/weekly-report", SiteController, :update_weekly_settings
|
||||
post "/sites/:website/weekly-report/recipients", SiteController, :add_weekly_report_recipient
|
||||
delete "/sites/:website/weekly-report/recipients/:recipient", SiteController, :remove_weekly_report_recipient
|
||||
post "/sites/:website/monthly-report/enable", SiteController, :enable_monthly_report
|
||||
post "/sites/:website/monthly-report/disable", SiteController, :disable_monthly_report
|
||||
put "/sites/:website/monthly-report", SiteController, :update_monthly_settings
|
||||
post "/sites/:website/monthly-report/recipients", SiteController, :add_monthly_report_recipient
|
||||
delete "/sites/:website/monthly-report/recipients/:recipient", SiteController, :remove_monthly_report_recipient
|
||||
|
||||
get "/sites/:website/weekly-report/unsubscribe", UnsubscribeController, :weekly_report
|
||||
get "/sites/:website/monthly-report/unsubscribe", UnsubscribeController, :monthly_report
|
||||
|
||||
get "/:website/snippet", SiteController, :add_snippet
|
||||
get "/:website/settings", SiteController, :settings
|
||||
get "/:website/goals", SiteController, :goals
|
||||
|
@ -149,7 +149,7 @@ body {
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 0px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#3d4852;font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:0px;padding-left:10px;">
|
||||
<div style="font-size: 12px; line-height: 14px; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #3d4852;">
|
||||
<p style="font-size: 14px; line-height: 19px; text-align: right; margin: 0;"><span style="font-size: 16px;"><span style="line-height: 19px; font-size: 16px;"><strong><span style="line-height: 19px; font-size: 16px;">Weekly report for <%= @site.domain %></span></strong></span></span></p>
|
||||
<p style="font-size: 14px; line-height: 19px; text-align: right; margin: 0;"><span style="font-size: 16px;"><span style="line-height: 19px; font-size: 16px;"><strong><span style="line-height: 19px; font-size: 16px;"><%= @name %> report for <%= @site.domain %></span></strong></span></span></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
@ -299,14 +299,43 @@ body {
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
<div style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
|
||||
<!--<![endif]-->
|
||||
<div></div>
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 5px; padding-left: 5px; padding-top: 5px; padding-bottom: 5px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#8795a1;font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;line-height:120%;padding-top:5px;padding-right:5px;padding-bottom:5px;padding-left:5px;">
|
||||
<div style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 14px; text-align: left; color: #8795a1;"><span style="font-size: 12px; line-height: 14px;"><strong>BOUNCE RATE</strong></span></div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 5px; padding-left: 5px; padding-top: 0px; padding-bottom: 5px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#555555;font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;line-height:120%;padding-top:0px;padding-right:5px;padding-bottom:5px;padding-left:5px;">
|
||||
<div style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 14px; color: #555555;">
|
||||
<p style="font-size: 12px; line-height: 14px; text-align: left; margin: 0;"><strong><span style="font-size: 20px; line-height: 24px;"><%= @bounce_rate %>%</span></strong></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 5px; padding-left: 5px; padding-top: 0px; padding-bottom: 5px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#1f9d55;font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;line-height:120%;padding-top:0px;padding-right:5px;padding-bottom:5px;padding-left:5px;">
|
||||
<%= cond do %>
|
||||
<% @change_bounce_rate == nil -> %>
|
||||
<div style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 14px; color: #8795a1;">
|
||||
<p style="font-size: 12px; line-height: 14px; text-align: left; margin: 0;"><strong>N/A</strong></p>
|
||||
</div>
|
||||
<% @change_bounce_rate <= 0 -> %>
|
||||
<div style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 14px; color: #1f9d55;">
|
||||
<p style="font-size: 12px; line-height: 14px; text-align: left; margin: 0;"><strong><%= @change_bounce_rate %>%</strong></p>
|
||||
</div>
|
||||
<% @change_bounce_rate > 0 -> %>
|
||||
<div style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 14px; color: #ff4457;">
|
||||
<p style="font-size: 12px; line-height: 14px; text-align: left; margin: 0;"><strong>+<%= @change_bounce_rate %>%</strong></p>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div>
|
||||
<!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
|
||||
<!--[if (mso)|(IE)]></td><td align="center" width="160" style="background-color:transparent;width:160px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -580,7 +609,7 @@ body {
|
||||
<!--<![endif]-->
|
||||
<div align="center" class="button-container" style="padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-spacing: 0; border-collapse: collapse; mso-table-lspace:0pt; mso-table-rspace:0pt;"><tr><td style="padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px" align="center"><v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="https://plausible.io/plausible.io" style="height:31.5pt; width:189.75pt; v-text-anchor:middle;" arcsize="10%" stroke="false" fillcolor="#5661b3"><w:anchorlock/><v:textbox inset="0,0,0,0"><center style="color:#ffffff; font-family:Arial, sans-serif; font-size:16px"><![endif]--><a href="<%= @view_link %>" style="-webkit-text-size-adjust: none; text-decoration: none; display: inline-block; color: #ffffff; background-color: #5661b3; border-radius: 4px; -webkit-border-radius: 4px; -moz-border-radius: 4px; width: auto; width: auto; border-top: 1px solid #5661b3; border-right: 1px solid #5661b3; border-bottom: 1px solid #5661b3; border-left: 1px solid #5661b3; padding-top: 5px; padding-bottom: 5px; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; text-align: center; mso-border-alt: none; word-break: keep-all;" target="_blank"><span style="padding-left:20px;padding-right:20px;font-size:16px;display:inline-block;">
|
||||
<span style="font-size: 16px; line-height: 32px;">View last 7 days on Plausible</span>
|
||||
<span style="font-size: 16px; line-height: 32px;">View on Plausible</span>
|
||||
</span></a>
|
||||
<!--[if mso]></center></v:textbox></v:roundrect></td></tr></table><![endif]-->
|
||||
</div>
|
||||
@ -642,7 +671,7 @@ body {
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
|
||||
<div style="color:#3d4852;font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
|
||||
<div style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 14px; color: #3d4852;">
|
||||
<p style="font-size: 14px; line-height: 14px; margin: 0;"><span style="font-size: 12px;">You are receiving this email because you have enabled weekly email reports for <%= link(@site.domain, rel: "nofollow", style: "text-decoration: none; color: #3d4852", to: "#") %>. <a href="<%= @settings_link %>" rel="noopener" style="text-decoration: underline; color: #0068A5;" target="_blank">Click here </a>to manage your notification settings.</span></p>
|
||||
<p style="font-size: 14px; line-height: 14px; margin: 0;"><span style="font-size: 12px;">Don't want to receive these emails? <a href="<%= @unsubscribe_link %>" rel="noopener" style="text-decoration: underline; color: #0068A5;" target="_blank">Click here</a> to unsubscribe.</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
|
@ -23,5 +23,7 @@
|
||||
<symbol id="feather-maximize" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/></symbol>
|
||||
|
||||
<symbol id="feather-download" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-download"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></symbol>
|
||||
|
||||
<symbol id="feather-mail" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-mail"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></symbol>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.9 KiB |
@ -1,16 +0,0 @@
|
||||
<%= form_for @conn, "/google", [class: "max-w-sm w-full mx-auto bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
|
||||
<h2>Connect to property</h2>
|
||||
<p class="text-grey-dark mt-4">Select the Google Search Console property you would like to pull keyword data from</p>
|
||||
|
||||
<div class="my-6">
|
||||
<%= label f, :domain, class: "block text-grey-darker text-sm font-bold mb-2" %>
|
||||
<div class="inline-block relative w-full">
|
||||
<%= select f, :domain, @verified_domains, class: "block appearance-none w-full bg-grey-lighter text-grey-darker cursor-pointer hover:border-grey p-2 pr-8 rounded leading-normal focus:outline-none" %>
|
||||
<div class="pointer-events-none absolute pin-y pin-r flex items-center px-2 text-red">
|
||||
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= submit "Enable Integration →", class: "button mt-4 w-full" %>
|
||||
<% end %>
|
@ -115,14 +115,12 @@
|
||||
</div>
|
||||
|
||||
<div class="max-w-md mx-auto bg-white shadow-md rounded rounded-t-none border-t-2 border-indigo-lightest px-8 pt-6 pb-8 mt-10" id="email-reports">
|
||||
<div>
|
||||
<h2>Email reports</h2>
|
||||
</div>
|
||||
<h2>Email reports</h2>
|
||||
|
||||
<div class="my-4 border-b border-grey-light"></div>
|
||||
|
||||
<div class="my-8 flex items-center">
|
||||
<%= if @weekly_report_changeset do %>
|
||||
<%= if @weekly_report do %>
|
||||
<%= button(to: "/sites/#{@site.domain}/weekly-report/disable", method: :post, class: "border rounded-full border-grey flex items-center cursor-pointer w-8 bg-green justify-end") do %>
|
||||
<span class="rounded-full border w-4 h-4 border-grey shadow-inner bg-white shadow"></span>
|
||||
<% end %>
|
||||
@ -131,22 +129,28 @@
|
||||
<span class="rounded-full border w-4 h-4 border-grey shadow-inner bg-white shadow"></span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<span class="ml-2">Receive a weekly email report every Monday</span>
|
||||
<span class="ml-2">Send a weekly email report every Monday</span>
|
||||
</div>
|
||||
<%= if @weekly_report_changeset do %>
|
||||
<%= if @weekly_report do %>
|
||||
<div class="text-sm text-grey-darker mt-6">
|
||||
<%= form_for @weekly_report_changeset, "/sites/#{@site.domain}/weekly-report", [class: "max-w-xs"], fn f -> %>
|
||||
<%= label f, :email, "Email address", class: "block text-grey-darker text-sm font-bold mb-2" %>
|
||||
<div class="flex">
|
||||
<%= email_input f, :email, class: "transition bg-grey-lighter appearance-none border border-transparent rounded w-full p-2 text-grey-darker leading-normal appearance-none focus:outline-none focus:border-grey-light", style: "flex-grow: 2" %>
|
||||
<%= submit "Update", class: "button rounded-l-none" %>
|
||||
<h4 class="my-2">Weekly report recipients</h4>
|
||||
<%= for recipient <- @weekly_report.recipients do %>
|
||||
<div class="p-3 flex justify-between bg-grey-lighter rounded my-2 max-w-sm">
|
||||
<span><svg class="feather mr-1" style="transform: translateY(0.05em)"><use xlink:href="#feather-mail" /></svg><%= recipient %></span>
|
||||
<%= button("❌", to: "/sites/#{@site.domain}/weekly-report/recipients/#{recipient}", method: :delete) %>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= form_for @conn, "/sites/#{@site.domain}/weekly-report/recipients", fn f -> %>
|
||||
<div class="flex justify-between my-2 max-w-sm">
|
||||
<%= email_input f, :recipient, class: "transition bg-grey-lighter appearance-none border border-transparent rounded w-full p-2 text-grey-darker leading-normal appearance-none focus:outline-none focus:border-grey-light", style: "flex-grow: 2", placeholder: "recipient@example.com" %>
|
||||
<%= submit "Add recipient", class: "button rounded-l-none whitespace-no-wrap" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="my-4 border-b border-grey-light"></div>
|
||||
<div class="my-8 border-b border-grey-light"></div>
|
||||
<div class="my-8 flex items-center">
|
||||
<%= if @monthly_report_changeset do %>
|
||||
<%= if @monthly_report do %>
|
||||
<%= button(to: "/sites/#{@site.domain}/monthly-report/disable", method: :post, class: "border rounded-full border-grey flex items-center cursor-pointer w-8 bg-green justify-end") do %>
|
||||
<span class="rounded-full border w-4 h-4 border-grey shadow-inner bg-white shadow"></span>
|
||||
<% end %>
|
||||
@ -155,15 +159,21 @@
|
||||
<span class="rounded-full border w-4 h-4 border-grey shadow-inner bg-white shadow"></span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<span class="ml-2">Receive a monthly email report on 1st of the month</span>
|
||||
<span class="ml-2">Send a monthly email report on 1st of the month</span>
|
||||
</div>
|
||||
<%= if @monthly_report_changeset do %>
|
||||
<%= if @monthly_report do %>
|
||||
<div class="text-sm text-grey-darker mt-6">
|
||||
<%= form_for @monthly_report_changeset, "/sites/#{@site.domain}/monthly-report", [class: "max-w-xs"], fn f -> %>
|
||||
<%= label f, :email, "Email address", class: "block text-grey-darker text-sm font-bold mb-2" %>
|
||||
<div class="flex">
|
||||
<%= email_input f, :email, class: "transition bg-grey-lighter appearance-none border border-transparent rounded w-full p-2 text-grey-darker leading-normal appearance-none focus:outline-none focus:border-grey-light", style: "flex-grow: 2" %>
|
||||
<%= submit "Update", class: "button rounded-l-none" %>
|
||||
<h4 class="my-2">Monthly report recipients</h4>
|
||||
<%= for recipient <- @monthly_report.recipients do %>
|
||||
<div class="p-3 flex justify-between bg-grey-lighter rounded my-2 max-w-sm">
|
||||
<span><svg class="feather mr-1" style="transform: translateY(0.05em)"><use xlink:href="#feather-mail" /></svg><%= recipient %></span>
|
||||
<%= button("❌", to: "/sites/#{@site.domain}/monthly-report/recipients/#{recipient}", method: :delete) %>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= form_for @conn, "/sites/#{@site.domain}/monthly-report/recipients", fn f -> %>
|
||||
<div class="flex justify-between my-2 max-w-sm">
|
||||
<%= email_input f, :recipient, class: "transition bg-grey-lighter appearance-none border border-transparent rounded w-full p-2 text-grey-darker leading-normal appearance-none focus:outline-none focus:border-grey-light", style: "flex-grow: 2", placeholder: "recipient@example.com" %>
|
||||
<%= submit "Add recipient", class: "button rounded-l-none whitespace-no-wrap" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
4
lib/plausible_web/templates/unsubscribe/success.html.eex
Normal file
4
lib/plausible_web/templates/unsubscribe/success.html.eex
Normal file
@ -0,0 +1,4 @@
|
||||
<div class="w-full max-w-sm mx-auto bg-white shadow-md rounded px-8 py-6 mt-8"]>
|
||||
<h2>Unsubscribe successful</h2>
|
||||
<p class="mt-4">You will no longer receive a <%= @interval %> analytics report for <%= @site %></p>
|
||||
</div>
|
3
lib/plausible_web/views/unsubscribe_view.ex
Normal file
3
lib/plausible_web/views/unsubscribe_view.ex
Normal file
@ -0,0 +1,3 @@
|
||||
defmodule PlausibleWeb.UnsubscribeView do
|
||||
use PlausibleWeb, :view
|
||||
end
|
28
priv/repo/migrations/20200121091251_add_recipients.exs
Normal file
28
priv/repo/migrations/20200121091251_add_recipients.exs
Normal file
@ -0,0 +1,28 @@
|
||||
defmodule Plausible.Repo.Migrations.AddRecipients do
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
alter table(:weekly_reports) do
|
||||
add :recipients, {:array, :citext}, null: false, default: []
|
||||
end
|
||||
|
||||
execute "UPDATE weekly_reports SET recipients = array_append(recipients, email)"
|
||||
|
||||
alter table(:weekly_reports) do
|
||||
remove :email
|
||||
end
|
||||
|
||||
alter table(:monthly_reports) do
|
||||
add :recipients, {:array, :citext}, null: false, default: []
|
||||
end
|
||||
|
||||
execute "UPDATE monthly_reports SET recipients = array_append(recipients, email)"
|
||||
|
||||
alter table(:monthly_reports) do
|
||||
remove :email
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
end
|
||||
end
|
95
test/mix/tasks/send_email_reports_test.exs
Normal file
95
test/mix/tasks/send_email_reports_test.exs
Normal file
@ -0,0 +1,95 @@
|
||||
defmodule Mix.Tasks.EmailReportsTest do
|
||||
use Plausible.DataCase
|
||||
use Bamboo.Test
|
||||
alias Mix.Tasks.SendEmailReports
|
||||
|
||||
describe "weekly reports" do
|
||||
test "sends weekly report on Monday 9am local timezone" do
|
||||
site = insert(:site, timezone: "US/Eastern")
|
||||
insert(:weekly_report, site: site, recipients: ["user@email.com"])
|
||||
time = Timex.now() |> Timex.beginning_of_week |> Timex.shift(hours: 14) # 2pm UTC is 10am EST
|
||||
|
||||
SendEmailReports.execute(time)
|
||||
|
||||
assert_email_delivered_with(subject: "Weekly report for #{site.domain}", to: [nil: "user@email.com"])
|
||||
end
|
||||
|
||||
test "does not send a report on Monday before 9am in local timezone" do
|
||||
site = insert(:site, timezone: "US/Eastern")
|
||||
insert(:weekly_report, site: site, recipients: ["user@email.com"])
|
||||
time = Timex.now() |> Timex.beginning_of_week |> Timex.shift(hours: 12) # 12pm UTC is 8am EST
|
||||
|
||||
SendEmailReports.execute(time)
|
||||
|
||||
assert_no_emails_delivered()
|
||||
end
|
||||
|
||||
test "does not send a report on Tuesday" do
|
||||
site = insert(:site)
|
||||
insert(:weekly_report, site: site, recipients: ["user@email.com"])
|
||||
time = Timex.now() |> Timex.beginning_of_week |> Timex.shift(days: 1, hours: 10)
|
||||
|
||||
SendEmailReports.execute(time)
|
||||
|
||||
assert_no_emails_delivered()
|
||||
end
|
||||
|
||||
test "does not send the same report multiple times on the same week" do
|
||||
site = insert(:site)
|
||||
insert(:weekly_report, site: site, recipients: ["user@email.com"])
|
||||
time = Timex.now() |> Timex.beginning_of_week |> Timex.shift(hours: 10)
|
||||
|
||||
SendEmailReports.execute(time)
|
||||
|
||||
assert_email_delivered_with(subject: "Weekly report for #{site.domain}", to: [nil: "user@email.com"])
|
||||
|
||||
SendEmailReports.execute(time)
|
||||
assert_no_emails_delivered()
|
||||
end
|
||||
end
|
||||
|
||||
describe "monthly_reports" do
|
||||
test "sends monthly report on the 1st of the month after 9am local timezone" do
|
||||
site = insert(:site, timezone: "US/Eastern")
|
||||
insert(:monthly_report, site: site, recipients: ["user@email.com"])
|
||||
{:ok, time, _} = DateTime.from_iso8601("2019-04-01T14:00:00Z")
|
||||
|
||||
SendEmailReports.execute(time)
|
||||
|
||||
assert_email_delivered_with(subject: "March report for #{site.domain}", to: [nil: "user@email.com"])
|
||||
end
|
||||
|
||||
test "does not send a report on the 1st of the month before 9am in local timezone" do
|
||||
site = insert(:site, timezone: "US/Eastern")
|
||||
insert(:monthly_report, site: site, recipients: ["user@email.com"])
|
||||
time = Timex.now() |> Timex.beginning_of_month |> Timex.shift(hours: 12) # 12pm UTC is 8am EST
|
||||
|
||||
SendEmailReports.execute(time)
|
||||
|
||||
assert_no_emails_delivered()
|
||||
end
|
||||
|
||||
test "does not send a report on the 2nd of the month" do
|
||||
site = insert(:site)
|
||||
insert(:monthly_report, site: site, recipients: ["user@email.com"])
|
||||
time = Timex.now() |> Timex.beginning_of_month |> Timex.shift(days: 1, hours: 10)
|
||||
|
||||
SendEmailReports.execute(time)
|
||||
|
||||
assert_no_emails_delivered()
|
||||
end
|
||||
|
||||
test "does not send the same report multiple times on the same month" do
|
||||
site = insert(:site)
|
||||
insert(:monthly_report, site: site, recipients: ["user@email.com"])
|
||||
{:ok, time, _} = DateTime.from_iso8601("2019-02-01T11:00:00Z")
|
||||
|
||||
SendEmailReports.execute(time)
|
||||
|
||||
assert_email_delivered_with(subject: "January report for #{site.domain}", to: [nil: "user@email.com"])
|
||||
|
||||
SendEmailReports.execute(time)
|
||||
assert_no_emails_delivered()
|
||||
end
|
||||
end
|
||||
end
|
@ -187,4 +187,102 @@ defmodule PlausibleWeb.SiteControllerTest do
|
||||
assert Repo.aggregate(Plausible.Goal, :count, :id) == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /sites/:website/weekly-report/enable" do
|
||||
setup [:create_user, :log_in, :create_site]
|
||||
|
||||
test "creates a weekly report record with the user email", %{conn: conn, site: site, user: user} do
|
||||
post(conn, "/sites/#{site.domain}/weekly-report/enable")
|
||||
|
||||
report = Repo.get_by(Plausible.Site.WeeklyReport, site_id: site.id)
|
||||
assert report.recipients == [user.email]
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /sites/:website/weekly-report/disable" do
|
||||
setup [:create_user, :log_in, :create_site]
|
||||
|
||||
test "deletes the weekly report record", %{conn: conn, site: site} do
|
||||
insert(:weekly_report, site: site)
|
||||
|
||||
post(conn, "/sites/#{site.domain}/weekly-report/disable")
|
||||
|
||||
refute Repo.get_by(Plausible.Site.WeeklyReport, site_id: site.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /sites/:website/weekly-report/recipients" do
|
||||
setup [:create_user, :log_in, :create_site]
|
||||
|
||||
test "adds a recipient to the weekly report", %{conn: conn, site: site} do
|
||||
insert(:weekly_report, site: site)
|
||||
|
||||
post(conn, "/sites/#{site.domain}/weekly-report/recipients", recipient: "user@email.com")
|
||||
|
||||
report = Repo.get_by(Plausible.Site.WeeklyReport, site_id: site.id)
|
||||
assert report.recipients == ["user@email.com"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "DELETE /sites/:website/weekly-report/recipients/:recipient" do
|
||||
setup [:create_user, :log_in, :create_site]
|
||||
|
||||
test "removes a recipient from the weekly report", %{conn: conn, site: site} do
|
||||
insert(:weekly_report, site: site, recipients: ["recipient@email.com"])
|
||||
|
||||
delete(conn, "/sites/#{site.domain}/weekly-report/recipients/recipient@email.com")
|
||||
|
||||
report = Repo.get_by(Plausible.Site.WeeklyReport, site_id: site.id)
|
||||
assert report.recipients == []
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /sites/:website/monthly-report/enable" do
|
||||
setup [:create_user, :log_in, :create_site]
|
||||
|
||||
test "creates a monthly report record with the user email", %{conn: conn, site: site, user: user} do
|
||||
post(conn, "/sites/#{site.domain}/monthly-report/enable")
|
||||
|
||||
report = Repo.get_by(Plausible.Site.MonthlyReport, site_id: site.id)
|
||||
assert report.recipients == [user.email]
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /sites/:website/monthly-report/disable" do
|
||||
setup [:create_user, :log_in, :create_site]
|
||||
|
||||
test "deletes the monthly report record", %{conn: conn, site: site} do
|
||||
insert(:monthly_report, site: site)
|
||||
|
||||
post(conn, "/sites/#{site.domain}/monthly-report/disable")
|
||||
|
||||
refute Repo.get_by(Plausible.Site.MonthlyReport, site_id: site.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /sites/:website/monthly-report/recipients" do
|
||||
setup [:create_user, :log_in, :create_site]
|
||||
|
||||
test "adds a recipient to the monthly report", %{conn: conn, site: site} do
|
||||
insert(:monthly_report, site: site)
|
||||
|
||||
post(conn, "/sites/#{site.domain}/monthly-report/recipients", recipient: "user@email.com")
|
||||
|
||||
report = Repo.get_by(Plausible.Site.MonthlyReport, site_id: site.id)
|
||||
assert report.recipients == ["user@email.com"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "DELETE /sites/:website/monthly-report/recipients/:recipient" do
|
||||
setup [:create_user, :log_in, :create_site]
|
||||
|
||||
test "removes a recipient from the monthly report", %{conn: conn, site: site} do
|
||||
insert(:monthly_report, site: site, recipients: ["recipient@email.com"])
|
||||
|
||||
delete(conn, "/sites/#{site.domain}/monthly-report/recipients/recipient@email.com")
|
||||
|
||||
report = Repo.get_by(Plausible.Site.MonthlyReport, site_id: site.id)
|
||||
assert report.recipients == []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -0,0 +1,32 @@
|
||||
defmodule PlausibleWeb.UnsubscribeControllerTest do
|
||||
use PlausibleWeb.ConnCase
|
||||
use Plausible.Repo
|
||||
|
||||
describe "GET /sites/:website/weekly-report/unsubscribe" do
|
||||
test "removes a recipient from the weekly report without them having to log in", %{conn: conn} do
|
||||
site = insert(:site)
|
||||
insert(:weekly_report, site: site, recipients: ["recipient@email.com"])
|
||||
|
||||
conn = get(conn, "/sites/#{site.domain}/weekly-report/unsubscribe?email=recipient@email.com")
|
||||
|
||||
assert html_response(conn, 200) =~ "Unsubscribe successful"
|
||||
|
||||
report = Repo.get_by(Plausible.Site.WeeklyReport, site_id: site.id)
|
||||
assert report.recipients == []
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /sites/:website/monthly-report/unsubscribe" do
|
||||
test "removes a recipient from the weekly report without them having to log in", %{conn: conn} do
|
||||
site = insert(:site)
|
||||
insert(:monthly_report, site: site, recipients: ["recipient@email.com"])
|
||||
|
||||
conn = get(conn, "/sites/#{site.domain}/monthly-report/unsubscribe?email=recipient@email.com")
|
||||
|
||||
assert html_response(conn, 200) =~ "Unsubscribe successful"
|
||||
|
||||
report = Repo.get_by(Plausible.Site.MonthlyReport, site_id: site.id)
|
||||
assert report.recipients == []
|
||||
end
|
||||
end
|
||||
end
|
@ -50,8 +50,7 @@ defmodule Plausible.Factory do
|
||||
%Plausible.Event{
|
||||
hostname: hostname,
|
||||
pathname: "/",
|
||||
new_visitor: true,
|
||||
user_id: UUID.uuid4(),
|
||||
new_visitor: true, user_id: UUID.uuid4(),
|
||||
}
|
||||
end
|
||||
|
||||
@ -90,4 +89,12 @@ defmodule Plausible.Factory do
|
||||
created: Timex.now()
|
||||
}
|
||||
end
|
||||
|
||||
def weekly_report_factory do
|
||||
%Plausible.Site.WeeklyReport{}
|
||||
end
|
||||
|
||||
def monthly_report_factory do
|
||||
%Plausible.Site.MonthlyReport{}
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user