diff options
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | config/config.exs | 8 | ||||
| -rw-r--r-- | config/description.exs | 14 | ||||
| -rw-r--r-- | config/test.exs | 2 | ||||
| -rw-r--r-- | docs/configuration/cheatsheet.md | 4 | ||||
| -rw-r--r-- | lib/pleroma/emails/new_users_digest_email.ex | 32 | ||||
| -rw-r--r-- | lib/pleroma/web/templates/email/new_users_digest.html.eex | 158 | ||||
| -rw-r--r-- | lib/pleroma/web/templates/layout/email_styled.html.eex | 193 | ||||
| -rw-r--r-- | lib/pleroma/web/views/email_view.ex | 4 | ||||
| -rw-r--r-- | lib/pleroma/workers/cron/new_users_digest_worker.ex | 60 | ||||
| -rw-r--r-- | test/workers/cron/new_users_digest_worker_test.exs | 32 | 
11 files changed, 506 insertions, 2 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e838983b..ce0f584cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).  - User notification settings: Add `privacy_option` option.  - Support for custom Elixir modules (such as MRF policies)  - User settings: Add _This account is a_ option. +- A new users admin digest email  - OAuth: admin scopes support (relevant setting: `[:auth, :enforce_oauth_admin_scope_usage]`).  <details>    <summary>API Changes</summary> diff --git a/config/config.exs b/config/config.exs index ccc0c4e52..664572cf3 100644 --- a/config/config.exs +++ b/config/config.exs @@ -480,13 +480,15 @@ config :pleroma, Oban,      transmogrifier: 20,      scheduled_activities: 10,      background: 5, -    attachments_cleanup: 5 +    attachments_cleanup: 5, +    new_users_digest: 1    ],    crontab: [      {"0 0 * * *", Pleroma.Workers.Cron.ClearOauthTokenWorker},      {"0 * * * *", Pleroma.Workers.Cron.StatsWorker},      {"* * * * *", Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker}, -    {"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker} +    {"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker}, +    {"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}    ]  config :pleroma, :workers, @@ -560,6 +562,8 @@ config :pleroma, Pleroma.Emails.UserEmail,      text_muted_color: "#b9b9ba"    } +config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: false +  config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics"  config :pleroma, Pleroma.ScheduledActivity, diff --git a/config/description.exs b/config/description.exs index 0c0f4af3c..53d980c83 100644 --- a/config/description.exs +++ b/config/description.exs @@ -2504,6 +2504,20 @@ config :pleroma, :config_description, [    },    %{      group: :pleroma, +    key: Pleroma.Emails.NewUsersDigestEmail, +    type: :group, +    description: "New users admin email digest", +    children: [ +      %{ +        key: :enabled, +        type: :boolean, +        description: "enables new users admin digest email when `true`", +        suggestions: [false] +      } +    ] +  }, +  %{ +    group: :pleroma,      key: :oauth2,      type: :group,      description: "Configure OAuth 2 provider capabilities", diff --git a/config/test.exs b/config/test.exs index 078c46205..6bea09380 100644 --- a/config/test.exs +++ b/config/test.exs @@ -94,6 +94,8 @@ config :pleroma, Pleroma.ReverseProxy.Client, Pleroma.ReverseProxy.ClientMock  config :pleroma, :modules, runtime_dir: "test/fixtures/modules" +config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: true +  if File.exists?("./config/test.secret.exs") do    import_config "test.secret.exs"  else diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 2bd935983..c9559fe85 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -501,6 +501,10 @@ Email notifications settings.  - `:logo` - a path to a custom logo. Set it to `nil` to use the default Pleroma logo.  - `:styling` - a map with color settings for email templates. +### Pleroma.Emails.NewUsersDigestEmail + +- `:enabled` - a boolean, enables new users admin digest email when `true`. Defaults to `false`. +  ## Background jobs  ### Oban diff --git a/lib/pleroma/emails/new_users_digest_email.ex b/lib/pleroma/emails/new_users_digest_email.ex new file mode 100644 index 000000000..7d16b807f --- /dev/null +++ b/lib/pleroma/emails/new_users_digest_email.ex @@ -0,0 +1,32 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Emails.NewUsersDigestEmail do +  use Phoenix.Swoosh, view: Pleroma.Web.EmailView, layout: {Pleroma.Web.LayoutView, :email_styled} + +  defp instance_notify_email do +    Pleroma.Config.get([:instance, :notify_email]) || Pleroma.Config.get([:instance, :email]) +  end + +  def new_users(to, users_and_statuses) do +    instance_name = Pleroma.Config.get([:instance, :name]) +    styling = Pleroma.Config.get([Pleroma.Emails.UserEmail, :styling]) + +    logo_url = +      Pleroma.Web.Endpoint.url() <> +        Pleroma.Config.get([:frontend_configurations, :pleroma_fe, :logo]) + +    new() +    |> to({to.name, to.email}) +    |> from({instance_name, instance_notify_email()}) +    |> subject("#{instance_name} New Users") +    |> render_body("new_users_digest.html", %{ +      title: "New Users", +      users_and_statuses: users_and_statuses, +      instance: instance_name, +      styling: styling, +      logo_url: logo_url +    }) +  end +end diff --git a/lib/pleroma/web/templates/email/new_users_digest.html.eex b/lib/pleroma/web/templates/email/new_users_digest.html.eex new file mode 100644 index 000000000..40d9b8381 --- /dev/null +++ b/lib/pleroma/web/templates/email/new_users_digest.html.eex @@ -0,0 +1,158 @@ +<%= for {user, total_statuses, latest_status} <- @users_and_statuses do %> +	<%# user card START %> +						<div style="background-color:transparent;"> +		<div class="block-grid mixed-two-up no-stack" +			style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;"> +			<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;"> +				<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]--> +				<!--[if (mso)|(IE)]><td align="center" width="147" style="background-color:<%= @styling.content_background_color%>;width:76px; 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: 20px; padding-top:5px; padding-bottom:5px;"><![endif]--> +				<div class="col num3" +					style="display: table-cell; vertical-align: top; max-width: 320px; min-width: 76px; width: 76px;"> +					<div style="width:100% !important;"> +						<!--[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: 20px;"> +							<!--<![endif]--> +							<div align="left" class="img-container left " +								style="padding-right: 0px;padding-left: 0px;"> +								<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr style="line-height:0px"><td style="padding-right: 0px;padding-left: 0px;" align="left"><![endif]--><img +									alt="<%= user.name %>" border="0" class="left " src="<%= avatar_url(user) %>" +									style="text-decoration: none; -ms-interpolation-mode: bicubic; border: 0; height: auto; width: 100%; max-width: 76px; display: block;" +									title="<%= user.name %>" width="76" /> +								<!--[if mso]></td></tr></table><![endif]--> +							</div> +							<!--[if (!mso)&(!IE)]><!--> +						</div> +						<!--<![endif]--> +					</div> +				</div> + +				<!--[if (mso)|(IE)]></td></tr></table><![endif]--> +				<!--[if (mso)|(IE)]></td><td align="center" width="442" style="background-color:<%= @styling.content_background_color%>;width:442px; 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 class="col num9" +					style="display: table-cell; vertical-align: top; min-width: 320px; max-width: 441px; width: 442px;"> +					<div style="width:100% !important;"> +						<!--[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]--> +							<!--[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:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;"> +								<div +									style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_color %>;"> +									<p style="font-size: 14px; line-height: 19px; margin: 0;"><span +											style="font-size: 16px; color: <%= @styling.text_color %>;"><%= user.name %></span></p> +									<p style="font-size: 14px; line-height: 19px; margin: 0;"><span +											style="font-size: 16px;"><%= link "@" <> user.nickname, style: "color: #{@styling.link_color};text-decoration: none;", to: admin_user_url(user) %></span></p> +									<p style="font-size: 14px; line-height: 19px; margin: 0;"><span +											style="font-size: 16px;">Total: <%= total_statuses %></span></p> +								</div> +							</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]--> +			</div> +		</div> +	</div> +	<%# user card END %> + +	<%= if latest_status do %> +		<div style="background-color:transparent;"> +				<div class="block-grid" +					style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;"> +					<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;"> +						<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]--> +						<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; 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: 15px; padding-left: 15px; padding-top:5px; padding-bottom:5px;"><![endif]--> +						<div class="col num12" +							style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;"> +							<div style="width:100% !important;"> +								<!--[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: 15px; padding-left: 15px;"> +									<!--<![endif]--> +									<!--[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:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;"> +										<div +											style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_color %>;"> +											<span style="font-size: 16px; line-height: 19px;"><%= raw latest_status.object.data["content"] %></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: 10px; padding-left: 15px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]--> +									<div +										style="color:<%= @styling.text_muted_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:15px;"> +										<div +											style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_muted_color %>;"> +											<p style="font-size: 14px; line-height: 16px; margin: 0;"><%= format_date latest_status.object.data["published"] %></p> +										</div> +									</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]--> +					</div> +				</div> +			</div> +	<% end %> +          <%# divider start %> +					<div style="background-color:transparent;"> +						<div class="block-grid" +							style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;"> +							<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;"> +								<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]--> +								<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; 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 class="col num12" +									style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;"> +									<div style="width:100% !important;"> +										<!--[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]--> +											<table border="0" cellpadding="0" cellspacing="0" class="divider" role="presentation" +												style="table-layout: fixed; vertical-align: top; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; min-width: 100%; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;" +												valign="top" width="100%"> +												<tbody> +													<tr style="vertical-align: top;" valign="top"> +														<td class="divider_inner" +															style="word-break: break-word; vertical-align: top; min-width: 100%; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px;" +															valign="top"> +															<table align="center" border="0" cellpadding="0" cellspacing="0" class="divider_content" +																height="0" role="presentation" +																style="table-layout: fixed; vertical-align: top; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; border-top: 1px solid <%= @styling.text_color %>; height: 0px;" +																valign="top" width="100%"> +																<tbody> +																	<tr style="vertical-align: top;" valign="top"> +																		<td height="0" +																			style="word-break: break-word; vertical-align: top; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;" +																			valign="top"><span></span></td> +																	</tr> +																</tbody> +															</table> +														</td> +													</tr> +												</tbody> +											</table> +											<!--[if (!mso)&(!IE)]><!--> +										</div> +										<!--<![endif]--> +									</div> +								</div> +								<!--[if (mso)|(IE)]></td></tr></table><![endif]--> +								<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]--> +							</div> +						</div> +					</div> + +          <%# divider end %> +	<%# user card END %> +<% end %> diff --git a/lib/pleroma/web/templates/layout/email_styled.html.eex b/lib/pleroma/web/templates/layout/email_styled.html.eex new file mode 100644 index 000000000..ca2caaf4d --- /dev/null +++ b/lib/pleroma/web/templates/layout/email_styled.html.eex @@ -0,0 +1,193 @@ +<!DOCTYPE html +	PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office" +	xmlns:v="urn:schemas-microsoft-com:vml"> + +<head> +	<!--[if gte mso 9]><xml><o:OfficeDocumentSettings><o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml><![endif]--> +	<meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> +	<meta content="width=device-width" name="viewport" /> +	<!--[if !mso]><!--> +	<meta content="IE=edge" http-equiv="X-UA-Compatible" /> +	<!--<![endif]--> +	<title><%= @email.subject %></title> +	<!--[if !mso]><!--> +	<!--<![endif]--> +	<style type="text/css"> +		body { +			margin: 0; +			padding: 0; +		} + +		a { + +			color: <%= @styling.link_color %>; +			text-decoration: none; +		} + +		table, +		td, +		tr { +			vertical-align: top; +			border-collapse: collapse; +		} + +		* { +			line-height: inherit; +		} + +		a[x-apple-data-detectors=true] { +			color: inherit !important; +			text-decoration: none !important; +		} +	</style> +	<style id="media-query" type="text/css"> +		@media (max-width: 610px) { + +			.block-grid, +			.col { +				min-width: 320px !important; +				max-width: 100% !important; +				display: block !important; +			} + +			.block-grid { +				width: 100% !important; +			} + +			.col { +				width: 100% !important; +			} + +			.col>div { +				margin: 0 auto; +			} + +			.no-stack .col { +				min-width: 0 !important; +				display: table-cell !important; +			} + +			.no-stack.two-up .col { +				width: 50% !important; +			} + +			.no-stack .col.num4 { +				width: 33% !important; +			} + +			.no-stack .col.num8 { +				width: 66% !important; +			} + +			.no-stack .col.num4 { +				width: 33% !important; +			} + +			.no-stack .col.num3 { +				width: 25% !important; +			} + +			.no-stack .col.num6 { +				width: 50% !important; +			} + +			.no-stack .col.num9 { +				width: 75% !important; +			} + +		} +	</style> +</head> + +<body class="clean-body" style="margin: 0; padding: 0; -webkit-text-size-adjust: 100%; background-color: <%= @styling.background_color %>;"> +	<!--[if IE]><div class="ie-browser"><![endif]--> +	<table bgcolor="<%= @styling.background_color %>" cellpadding="0" cellspacing="0" class="nl-container" role="presentation" +		style="table-layout: fixed; vertical-align: top; min-width: 320px; Margin: 0 auto; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: <%= @styling.background_color %>; width: 100%;" +		valign="top" width="100%"> +		<tbody> +			<tr style="vertical-align: top;" valign="top"> +				<td style="word-break: break-word; vertical-align: top;" valign="top"> +					<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td align="center" style="background-color:<%= @styling.background_color %>"><![endif]--> + +					<%# header %> +					<div style="background-color:transparent;"> +						<div class="block-grid" +							style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;"> +							<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;"> +								<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]--> +								<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; 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 class="col num12" +									style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;"> +									<div style="width:100% !important;"> +										<!--[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 align="center" class="img-container center" +												style="padding-right: 0px;padding-left: 0px;"> +												<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr style="line-height:0px"><td style="padding-right: 0px;padding-left: 0px;" align="center"><![endif]--><img +													align="center" alt="Image" border="0" class="center" src="<%= @logo_url %>" +													style="text-decoration: none; -ms-interpolation-mode: bicubic; border: 0; height: 80px; width: auto; max-height: 80px; display: block;" +													title="Image" height="80" /> +												<!--[if mso]></td></tr></table><![endif]--> +											</div> +											<!--[if (!mso)&(!IE)]><!--> +										</div> +										<!--<![endif]--> +									</div> +								</div> +								<!--[if (mso)|(IE)]></td></tr></table><![endif]--> +								<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]--> +							</div> +						</div> +					</div> + + +					<%# title %> +					<%= if @title do %> +						<div style="background-color:transparent;"> +							<div class="block-grid" +								style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;"> +								<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;"> +									<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]--> +									<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; 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 class="col num12" +										style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;"> +										<div style="width:100% !important;"> +											<!--[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]--> +												<!--[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="line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;"> +													<div +														style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height: 14px; color: <%= @styling.header_color %>;"> +														<p style="line-height: 36px; text-align: center; margin: 0;"><span +																style="font-size: 30px; color: <%= @styling.header_color %>;"><%= @title %></span></p> +													</div> +												</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]--> +								</div> +							</div> +						</div> +					<% end %> +					<%= render @view_module, @view_template, assigns %> + +				</td> +			</tr> +		</tbody> +	</table> +	<!--[if (IE)]></div><![endif]--> +</body> + +</html> diff --git a/lib/pleroma/web/views/email_view.ex b/lib/pleroma/web/views/email_view.ex index b506a234b..6b0fbe61e 100644 --- a/lib/pleroma/web/views/email_view.ex +++ b/lib/pleroma/web/views/email_view.ex @@ -12,4 +12,8 @@ defmodule Pleroma.Web.EmailView do      |> Timex.parse!("{ISO:Extended:Z}")      |> Timex.format!("{Mshort} {D}, {YYYY} {h24}:{m}")    end + +  def admin_user_url(%{id: id}) do +    Pleroma.Web.Endpoint.url() <> "/pleroma/admin/#/users/" <> id +  end  end diff --git a/lib/pleroma/workers/cron/new_users_digest_worker.ex b/lib/pleroma/workers/cron/new_users_digest_worker.ex new file mode 100644 index 000000000..951c2c054 --- /dev/null +++ b/lib/pleroma/workers/cron/new_users_digest_worker.ex @@ -0,0 +1,60 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.Cron.NewUsersDigestWorker do +  alias Pleroma.Activity +  alias Pleroma.Repo +  alias Pleroma.User + +  import Ecto.Query + +  use Pleroma.Workers.WorkerHelper, queue: "new_users_digest" + +  @impl Oban.Worker +  def perform(_args, _job) do +    if Pleroma.Config.get([Pleroma.Emails.NewUsersDigestEmail, :enabled]) do +      today = NaiveDateTime.utc_now() |> Timex.beginning_of_day() + +      a_day_ago = +        today +        |> Timex.shift(days: -1) +        |> Timex.beginning_of_day() + +      users_and_statuses = +        %{ +          local: true, +          order_by: :inserted_at +        } +        |> User.Query.build() +        |> where([u], u.inserted_at >= ^a_day_ago and u.inserted_at < ^today) +        |> Repo.all() +        |> Enum.map(fn user -> +          latest_status = +            Activity +            |> Activity.Queries.by_actor(user.ap_id) +            |> Activity.Queries.by_type("Create") +            |> Activity.with_preloaded_object() +            |> order_by(desc: :inserted_at) +            |> limit(1) +            |> Repo.one() + +          total_statuses = +            Activity +            |> Activity.Queries.by_actor(user.ap_id) +            |> Activity.Queries.by_type("Create") +            |> Repo.aggregate(:count, :id) + +          {user, total_statuses, latest_status} +        end) + +      if users_and_statuses != [] do +        %{is_admin: true} +        |> User.Query.build() +        |> Repo.all() +        |> Enum.map(&Pleroma.Emails.NewUsersDigestEmail.new_users(&1, users_and_statuses)) +        |> Enum.each(&Pleroma.Emails.Mailer.deliver/1) +      end +    end +  end +end diff --git a/test/workers/cron/new_users_digest_worker_test.exs b/test/workers/cron/new_users_digest_worker_test.exs new file mode 100644 index 000000000..2f439c1fe --- /dev/null +++ b/test/workers/cron/new_users_digest_worker_test.exs @@ -0,0 +1,32 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.Cron.NewUsersDigestWorkerTest do +  use Pleroma.DataCase +  import Pleroma.Factory + +  alias Pleroma.Tests.ObanHelpers +  alias Pleroma.Web.CommonAPI +  alias Pleroma.Workers.Cron.NewUsersDigestWorker + +  test "it sends new users digest emails" do +    yesterday = NaiveDateTime.utc_now() |> Timex.shift(days: -1) +    admin = insert(:user, %{is_admin: true}) +    user = insert(:user, %{inserted_at: yesterday}) +    user2 = insert(:user, %{inserted_at: yesterday}) +    CommonAPI.post(user, %{"status" => "cofe"}) + +    NewUsersDigestWorker.perform(nil, nil) +    ObanHelpers.perform_all() + +    assert_received {:email, email} +    assert email.to == [{admin.name, admin.email}] +    assert email.subject == "#{Pleroma.Config.get([:instance, :name])} New Users" + +    refute email.html_body =~ admin.nickname +    assert email.html_body =~ user.nickname +    assert email.html_body =~ user2.nickname +    assert email.html_body =~ "cofe" +  end +end  | 
