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]`).
API Changes 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 @@ 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 @@ 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 @@ -2502,6 +2502,20 @@ } ] }, + %{ + 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, 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, :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 +# 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 %> +
+
+
+ + +
+
+ +
+ +
+ <%= user.name %> + +
+ +
+ +
+
+ + + +
+
+ +
+ + +
+
+

<%= user.name %>

+

<%= link "@" <> user.nickname, style: "color: #{@styling.link_color};text-decoration: none;", to: admin_user_url(user) %>

+

Total: <%= total_statuses %>

+
+
+ + +
+ +
+
+ + +
+
+
+ <%# user card END %> + + <%= if latest_status do %> +
+
+
+ + +
+
+ +
+ + +
+
+ <%= raw latest_status.object.data["content"] %>
+
+ + +
+
+

<%= format_date latest_status.object.data["published"] %>

+
+
+ + +
+ +
+
+ + +
+
+
+ <% end %> + <%# divider start %> +
+
+
+ + +
+
+ +
+ + + + + + + + + +
+ +
+
+ + +
+
+
+ + <%# 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 @@ + + + + + + + + + + + + <%= @email.subject %> + + + + + + + + + + + + + + + + + + + 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 @@ def format_date(date) when is_binary(date) 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 +# 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 +# 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