Compare commits

...

16 Commits

Author SHA1 Message Date
Alex Gleason
2da6cb2206
Changelog: email list 2021-06-15 13:21:19 -05:00
Alex Gleason
3690664c69
Merge remote-tracking branch 'pleroma/develop' into email-list 2021-06-15 13:21:14 -05:00
Alex Gleason
31a510d6a4
EmailList: ensure that deactivated, unapproved, and unconfirmed users aren't subscribers 2021-06-15 13:19:24 -05:00
Alex Gleason
8c62cc95e6
EmailList: add combined.csv view to display all candidates with subscription status 2021-06-15 12:27:34 -05:00
Alex Gleason
73ed23e2fd
EmailList: add Subscribed? column 2021-06-15 12:16:14 -05:00
Alex Gleason
385d432ce7
EmailList: export user nickname 2021-06-15 12:09:50 -05:00
Alex Gleason
53796b6344
Return email_list as a nodeinfo feature 2021-06-15 11:48:42 -05:00
Alex Gleason
6e7b220549
EmailList: add unsubscribers csv, fix query 2021-06-14 21:07:12 -05:00
Alex Gleason
1f3dd2115c
EmailList: fix tests 2021-06-14 20:29:49 -05:00
Alex Gleason
98502836f5
EmailList: ApiSpec typofix 2021-06-14 20:23:25 -05:00
Alex Gleason
2f40a92647
accepts_newsletter --> accepts_email_list 2021-06-14 20:20:08 -05:00
Alex Gleason
9ec3865725
MailingList --> EmailList 2021-06-14 20:18:57 -05:00
Alex Gleason
90df530dec
AdminAPI: get email list subscribers 2021-06-14 20:08:13 -05:00
Alex Gleason
784b8b5f83
Build CSV from subscriber list 2021-06-14 19:24:41 -05:00
Alex Gleason
6109532083
Return accepts_newsletter to account owner only 2021-06-14 19:24:41 -05:00
Alex Gleason
a0f2cba0d8
Add accepts_newsletter field to User, set during registration or account updates 2021-06-14 19:24:36 -05:00
21 changed files with 401 additions and 8 deletions

View File

@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- MRF (`FollowBotPolicy`): New MRF Policy which makes a designated local Bot account attempt to follow all users in public Notes received by your instance. Users who require approving follower requests or have #nobot in their profile are excluded. - MRF (`FollowBotPolicy`): New MRF Policy which makes a designated local Bot account attempt to follow all users in public Notes received by your instance. Users who require approving follower requests or have #nobot in their profile are excluded.
- Return OAuth token `id` (primary key) in POST `/oauth/token`. - Return OAuth token `id` (primary key) in POST `/oauth/token`.
- Let users opt-in to receiving admin emails.
- `AnalyzeMetadata` upload filter for extracting image/video attachment dimensions and generating blurhashes for images. Blurhashes for videos are not generated at this time. - `AnalyzeMetadata` upload filter for extracting image/video attachment dimensions and generating blurhashes for images. Blurhashes for videos are not generated at this time.
- Attachment dimensions and blurhashes are federated when available. - Attachment dimensions and blurhashes are federated when available.
- Pinned posts federation - Pinned posts federation

View File

@ -148,6 +148,7 @@ defmodule Pleroma.User do
field(:accepts_chat_messages, :boolean, default: nil) field(:accepts_chat_messages, :boolean, default: nil)
field(:last_active_at, :naive_datetime) field(:last_active_at, :naive_datetime)
field(:disclose_client, :boolean, default: true) field(:disclose_client, :boolean, default: true)
field(:accepts_email_list, :boolean, default: false)
field(:pinned_objects, :map, default: %{}) field(:pinned_objects, :map, default: %{})
embeds_one( embeds_one(
@ -525,7 +526,8 @@ defmodule Pleroma.User do
:is_discoverable, :is_discoverable,
:actor_type, :actor_type,
:accepts_chat_messages, :accepts_chat_messages,
:disclose_client :disclose_client,
:accepts_email_list
] ]
) )
|> unique_constraint(:nickname) |> unique_constraint(:nickname)
@ -688,7 +690,8 @@ defmodule Pleroma.User do
:name, :name,
:nickname, :nickname,
:email, :email,
:accepts_chat_messages :accepts_chat_messages,
:accepts_email_list
]) ])
|> validate_required([:name, :nickname]) |> validate_required([:name, :nickname])
|> unique_constraint(:nickname) |> unique_constraint(:nickname)
@ -732,7 +735,8 @@ defmodule Pleroma.User do
:password_confirmation, :password_confirmation,
:emoji, :emoji,
:accepts_chat_messages, :accepts_chat_messages,
:registration_reason :registration_reason,
:accepts_email_list
]) ])
|> validate_required([:name, :nickname, :password, :password_confirmation]) |> validate_required([:name, :nickname, :password, :password_confirmation])
|> validate_confirmation(:password) |> validate_confirmation(:password)
@ -1724,7 +1728,8 @@ defmodule Pleroma.User do
fields: [], fields: [],
raw_fields: [], raw_fields: [],
is_discoverable: false, is_discoverable: false,
also_known_as: [] also_known_as: [],
accepts_email_list: false
}) })
end end

View File

@ -0,0 +1,70 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.EmailList do
@moduledoc """
Functions for generating email lists from local users.
"""
import Ecto.Query
alias Pleroma.Repo
alias Pleroma.User
@header_row ["Email Address", "Nickname", "Subscribe?"]
defp query(:subscribers) do
User.Query.build(%{
local: true,
active: true,
accepts_email_list: true
})
|> where([u], not is_nil(u.email))
end
defp query(:unsubscribers) do
User.Query.build(%{
local: true,
accepts_email_list: false
})
|> where([u], not is_nil(u.email))
end
defp query(:combined) do
User.Query.build(%{
local: true
})
|> where([u], not is_nil(u.email))
end
def generate_csv(audience) when is_atom(audience) do
audience
|> query()
|> generate_csv()
end
def generate_csv(%Ecto.Query{} = query) do
query
|> Repo.all()
|> Enum.map(&build_row/1)
|> build_csv()
end
defp subscribe?(%User{} = user) do
user.accepts_email_list && user.is_active && user.is_approved && user.is_confirmed
end
defp build_row(%User{} = user) do
[
user.email,
user.nickname,
subscribe?(user)
]
end
defp build_csv(lines) do
[@header_row | lines]
|> CSV.encode()
|> Enum.join()
end
end

View File

@ -124,6 +124,10 @@ defmodule Pleroma.User.Query do
where(query, [u], u.is_moderator == ^bool) where(query, [u], u.is_moderator == ^bool)
end end
defp compose_query({:accepts_email_list, bool}, query) do
where(query, [u], u.accepts_email_list == ^bool)
end
defp compose_query({:super_users, _}, query) do defp compose_query({:super_users, _}, query) do
where( where(
query, query,

View File

@ -0,0 +1,35 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.EmailListController do
use Pleroma.Web, :controller
alias Pleroma.User.EmailList
alias Pleroma.Web.Plugs.OAuthScopesPlug
require Logger
plug(OAuthScopesPlug, %{scopes: ["admin:read:accounts"]})
def subscribers(conn, _params) do
render_csv(conn, :subscribers)
end
def unsubscribers(conn, _params) do
render_csv(conn, :unsubscribers)
end
def combined(conn, _params) do
render_csv(conn, :combined)
end
defp render_csv(conn, audience) when is_atom(audience) do
csv = EmailList.generate_csv(audience)
conn
|> put_resp_content_type("text/csv")
|> resp(200, csv)
|> send_resp()
end
end

View File

@ -458,6 +458,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
type: :string, type: :string,
nullable: true, nullable: true,
description: "Invite token required when the registrations aren't public" description: "Invite token required when the registrations aren't public"
},
accepts_email_list: %Schema{
allOf: [BooleanLike],
description:
"Whether the user opts-in to receiving news and marketing updates from site admins."
} }
}, },
example: %{ example: %{
@ -635,7 +640,12 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
description: description:
"Discovery (listing, indexing) of this account by external services (search bots etc.) is allowed." "Discovery (listing, indexing) of this account by external services (search bots etc.) is allowed."
}, },
actor_type: ActorType actor_type: ActorType,
accepts_email_list: %Schema{
allOf: [BooleanLike],
description:
"Whether the user opts-in to receiving news and marketing updates from site admins."
}
}, },
example: %{ example: %{
bot: false, bot: false,

View File

@ -184,7 +184,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
:skip_thread_containment, :skip_thread_containment,
:allow_following_move, :allow_following_move,
:also_known_as, :also_known_as,
:accepts_chat_messages :accepts_chat_messages,
:accepts_email_list
] ]
|> Enum.reduce(%{}, fn key, acc -> |> Enum.reduce(%{}, fn key, acc ->
Maps.put_if_present(acc, key, params[key], &{:ok, Params.truthy_param?(&1)}) Maps.put_if_present(acc, key, params[key], &{:ok, Params.truthy_param?(&1)})

View File

@ -292,6 +292,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|> maybe_put_allow_following_move(user, opts[:for]) |> maybe_put_allow_following_move(user, opts[:for])
|> maybe_put_unread_conversation_count(user, opts[:for]) |> maybe_put_unread_conversation_count(user, opts[:for])
|> maybe_put_unread_notification_count(user, opts[:for]) |> maybe_put_unread_notification_count(user, opts[:for])
|> maybe_put_accepts_email_list(user, opts[:for])
|> maybe_put_email_address(user, opts[:for]) |> maybe_put_email_address(user, opts[:for])
end end
@ -404,6 +405,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
defp maybe_put_unread_notification_count(data, _, _), do: data defp maybe_put_unread_notification_count(data, _, _), do: data
defp maybe_put_accepts_email_list(data, %User{id: user_id}, %User{id: user_id} = user) do
Kernel.put_in(
data,
[:pleroma, :accepts_email_list],
user.accepts_email_list
)
end
defp maybe_put_accepts_email_list(data, _, _), do: data
defp maybe_put_email_address(data, %User{id: user_id}, %User{id: user_id} = user) do defp maybe_put_email_address(data, %User{id: user_id}, %User{id: user_id} = user) do
Kernel.put_in( Kernel.put_in(
data, data,

View File

@ -83,7 +83,8 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
"safe_dm_mentions" "safe_dm_mentions"
end, end,
"pleroma_emoji_reactions", "pleroma_emoji_reactions",
"pleroma_chat_messages" "pleroma_chat_messages",
"email_list"
] ]
|> Enum.filter(& &1) |> Enum.filter(& &1)
end end

View File

@ -261,6 +261,10 @@ defmodule Pleroma.Web.Router do
post("/frontends/install", FrontendController, :install) post("/frontends/install", FrontendController, :install)
post("/backups", AdminAPIController, :create_backup) post("/backups", AdminAPIController, :create_backup)
get("/email_list/subscribers.csv", EmailListController, :subscribers)
get("/email_list/unsubscribers.csv", EmailListController, :unsubscribers)
get("/email_list/combined.csv", EmailListController, :combined)
end end
scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do

View File

@ -14,7 +14,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
def register_user(params, opts \\ []) do def register_user(params, opts \\ []) do
params = params =
params params
|> Map.take([:email, :token, :password]) |> Map.take([:email, :token, :password, :accepts_email_list])
|> Map.put(:bio, params |> Map.get(:bio, "") |> User.parse_bio()) |> Map.put(:bio, params |> Map.get(:bio, "") |> User.parse_bio())
|> Map.put(:nickname, params[:username]) |> Map.put(:nickname, params[:username])
|> Map.put(:name, Map.get(params, :fullname, params[:username])) |> Map.put(:name, Map.get(params, :fullname, params[:username]))

View File

@ -133,6 +133,7 @@ defmodule Pleroma.Mixfile do
{:phoenix_html, "~> 2.14"}, {:phoenix_html, "~> 2.14"},
{:calendar, "~> 1.0"}, {:calendar, "~> 1.0"},
{:cachex, "~> 3.2"}, {:cachex, "~> 3.2"},
{:csv, "~> 2.4"},
{:poison, "~> 3.0", override: true}, {:poison, "~> 3.0", override: true},
{:tesla, "~> 1.4.0", override: true}, {:tesla, "~> 1.4.0", override: true},
{:castore, "~> 0.1"}, {:castore, "~> 0.1"},

View File

@ -23,6 +23,7 @@
"credo": {:hex, :credo, "1.5.5", "e8f422026f553bc3bebb81c8e8bf1932f498ca03339856c7fec63d3faac8424b", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "dd8623ab7091956a855dc9f3062486add9c52d310dfd62748779c4315d8247de"}, "credo": {:hex, :credo, "1.5.5", "e8f422026f553bc3bebb81c8e8bf1932f498ca03339856c7fec63d3faac8424b", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "dd8623ab7091956a855dc9f3062486add9c52d310dfd62748779c4315d8247de"},
"crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, "crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
"crypt": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/crypt.git", "cf2aa3f11632e8b0634810a15b3e612c7526f6a3", [ref: "cf2aa3f11632e8b0634810a15b3e612c7526f6a3"]}, "crypt": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/crypt.git", "cf2aa3f11632e8b0634810a15b3e612c7526f6a3", [ref: "cf2aa3f11632e8b0634810a15b3e612c7526f6a3"]},
"csv": {:hex, :csv, "2.4.1", "50e32749953b6bf9818dbfed81cf1190e38cdf24f95891303108087486c5925e", [:mix], [{:parallel_stream, "~> 1.0.4", [hex: :parallel_stream, repo: "hexpm", optional: false]}], "hexpm", "54508938ac67e27966b10ef49606e3ad5995d665d7fc2688efb3eab1307c9079"},
"custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"}, "custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"},
"db_connection": {:hex, :db_connection, "2.4.0", "d04b1b73795dae60cead94189f1b8a51cc9e1f911c234cc23074017c43c031e5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad416c21ad9f61b3103d254a71b63696ecadb6a917b36f563921e0de00d7d7c8"}, "db_connection": {:hex, :db_connection, "2.4.0", "d04b1b73795dae60cead94189f1b8a51cc9e1f911c234cc23074017c43c031e5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad416c21ad9f61b3103d254a71b63696ecadb6a917b36f563921e0de00d7d7c8"},
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
@ -88,6 +89,7 @@
"oban": {:hex, :oban, "2.3.4", "ec7509b9af2524d55f529cb7aee93d36131ae0bf0f37706f65d2fe707f4d9fd8", [:mix], [{:ecto_sql, ">= 3.4.3", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c70ca0434758fd1805422ea4446af5e910ddc697c0c861549c8f0eb0cfbd2fdf"}, "oban": {:hex, :oban, "2.3.4", "ec7509b9af2524d55f529cb7aee93d36131ae0bf0f37706f65d2fe707f4d9fd8", [:mix], [{:ecto_sql, ">= 3.4.3", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c70ca0434758fd1805422ea4446af5e910ddc697c0c861549c8f0eb0cfbd2fdf"},
"open_api_spex": {:hex, :open_api_spex, "3.10.0", "94e9521ad525b3fcf6dc77da7c45f87fdac24756d4de588cb0816b413e7c1844", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "2dbb2bde3d2b821f06936e8dfaf3284331186556291946d84eeba3750ac28765"}, "open_api_spex": {:hex, :open_api_spex, "3.10.0", "94e9521ad525b3fcf6dc77da7c45f87fdac24756d4de588cb0816b413e7c1844", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "2dbb2bde3d2b821f06936e8dfaf3284331186556291946d84eeba3750ac28765"},
"p1_utils": {:hex, :p1_utils, "1.0.18", "3fe224de5b2e190d730a3c5da9d6e8540c96484cf4b4692921d1e28f0c32b01c", [:rebar3], [], "hexpm", "1fc8773a71a15553b179c986b22fbeead19b28fe486c332d4929700ffeb71f88"}, "p1_utils": {:hex, :p1_utils, "1.0.18", "3fe224de5b2e190d730a3c5da9d6e8540c96484cf4b4692921d1e28f0c32b01c", [:rebar3], [], "hexpm", "1fc8773a71a15553b179c986b22fbeead19b28fe486c332d4929700ffeb71f88"},
"parallel_stream": {:hex, :parallel_stream, "1.0.6", "b967be2b23f0f6787fab7ed681b4c45a215a81481fb62b01a5b750fa8f30f76c", [:mix], [], "hexpm", "639b2e8749e11b87b9eb42f2ad325d161c170b39b288ac8d04c4f31f8f0823eb"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"}, "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"},
"phoenix": {:hex, :phoenix, "1.5.9", "a6368d36cfd59d917b37c44386e01315bc89f7609a10a45a22f47c007edf2597", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7e4bce20a67c012f1fbb0af90e5da49fa7bf0d34e3a067795703b74aef75427d"}, "phoenix": {:hex, :phoenix, "1.5.9", "a6368d36cfd59d917b37c44386e01315bc89f7609a10a45a22f47c007edf2597", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7e4bce20a67c012f1fbb0af90e5da49fa7bf0d34e3a067795703b74aef75427d"},

View File

@ -0,0 +1,9 @@
defmodule Pleroma.Repo.Migrations.AddEmailListFieldToUsers do
use Ecto.Migration
def change do
alter table(:users) do
add(:accepts_email_list, :boolean, default: false)
end
end
end

View File

@ -0,0 +1,63 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.EmailListTest do
alias Pleroma.User.EmailList
use Pleroma.DataCase
import Pleroma.Factory
test "generate_csv/1 with :subscribers" do
user1 = insert(:user, accepts_email_list: true)
user2 = insert(:user, accepts_email_list: true)
user3 = insert(:user, accepts_email_list: true)
insert(:user, accepts_email_list: false)
expected = """
Email Address,Nickname,Subscribe?\r
#{user1.email},#{user1.nickname},true\r
#{user2.email},#{user2.nickname},true\r
#{user3.email},#{user3.nickname},true\r
"""
assert EmailList.generate_csv(:subscribers) == expected
end
test "generate_csv/1 with :unsubscribers" do
user1 = insert(:user, accepts_email_list: false)
user2 = insert(:user, accepts_email_list: false)
insert(:user, accepts_email_list: true)
insert(:user, accepts_email_list: true)
expected = """
Email Address,Nickname,Subscribe?\r
#{user1.email},#{user1.nickname},false\r
#{user2.email},#{user2.nickname},false\r
"""
assert EmailList.generate_csv(:unsubscribers) == expected
end
test "generate_csv/1 with :combined" do
user1 = insert(:user, accepts_email_list: true, is_active: false)
user2 = insert(:user, accepts_email_list: true)
user3 = insert(:user, accepts_email_list: true, is_approved: false)
user4 = insert(:user, accepts_email_list: true, is_confirmed: false)
user5 = insert(:user, accepts_email_list: true)
user6 = insert(:user, accepts_email_list: false)
expected = """
Email Address,Nickname,Subscribe?\r
#{user1.email},#{user1.nickname},false\r
#{user2.email},#{user2.nickname},true\r
#{user3.email},#{user3.nickname},false\r
#{user4.email},#{user4.nickname},false\r
#{user5.email},#{user5.nickname},true\r
#{user6.email},#{user6.nickname},false\r
"""
assert EmailList.generate_csv(:combined) == expected
end
end

View File

@ -681,6 +681,16 @@ defmodule Pleroma.UserTest do
assert user.is_confirmed assert user.is_confirmed
end end
test "it sets 'accepts_email_list'" do
params = Map.put_new(@full_user_data, :accepts_email_list, true)
changeset = User.register_changeset(%User{}, params)
assert changeset.valid?
{:ok, user} = Repo.insert(changeset)
assert user.accepts_email_list
end
end end
describe "user registration, with :account_activation_required" do describe "user registration, with :account_activation_required" do
@ -755,6 +765,17 @@ defmodule Pleroma.UserTest do
end end
end end
describe "update_changeset/2" do
test "it sets :accepts_email_list" do
changeset =
%User{accepts_email_list: false}
|> User.update_changeset(%{accepts_email_list: true})
assert changeset.valid?
assert %User{accepts_email_list: true} = Ecto.Changeset.apply_changes(changeset)
end
end
describe "get_or_fetch/1" do describe "get_or_fetch/1" do
test "gets an existing user by nickname" do test "gets an existing user by nickname" do
user = insert(:user) user = insert(:user)

View File

@ -0,0 +1,102 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.EmailListControllerTest do
use Pleroma.Web.ConnCase, async: true
import Pleroma.Factory
defp admin_setup do
admin = insert(:user, is_admin: true)
token = insert(:oauth_admin_token, user: admin)
conn =
build_conn()
|> assign(:user, admin)
|> assign(:token, token)
{:ok, %{admin: admin, token: token, conn: conn}}
end
defp user_setup do
user = insert(:user)
token = insert(:oauth_token, user: user)
conn =
build_conn()
|> assign(:user, user)
|> assign(:token, token)
{:ok, %{user: user, token: token, conn: conn}}
end
describe "GET /api/v1/pleroma/admin/email_list/subscribers.csv" do
setup do: admin_setup()
test "returns a CSV", %{conn: conn} do
result =
conn
|> get("/api/v1/pleroma/admin/email_list/subscribers.csv")
|> response(200)
assert result
end
end
describe "GET /api/v1/pleroma/admin/email_list/subscribers.csv unauthorized" do
setup do: user_setup()
test "returns 403", %{conn: conn} do
conn
|> get("/api/v1/pleroma/admin/email_list/subscribers.csv")
|> response(403)
end
end
describe "GET /api/v1/pleroma/admin/email_list/unsubscribers.csv" do
setup do: admin_setup()
test "returns a CSV", %{conn: conn} do
result =
conn
|> get("/api/v1/pleroma/admin/email_list/unsubscribers.csv")
|> response(200)
assert result
end
end
describe "GET /api/v1/pleroma/admin/email_list/unsubscribers.csv unauthorized" do
setup do: user_setup()
test "returns 403", %{conn: conn} do
conn
|> get("/api/v1/pleroma/admin/email_list/unsubscribers.csv")
|> response(403)
end
end
describe "GET /api/v1/pleroma/admin/email_list/combined.csv" do
setup do: admin_setup()
test "returns a CSV", %{conn: conn} do
result =
conn
|> get("/api/v1/pleroma/admin/email_list/combined.csv")
|> response(200)
assert result
end
end
describe "GET /api/v1/pleroma/admin/email_list/combined.csv unauthorized" do
setup do: user_setup()
test "returns 403", %{conn: conn} do
conn
|> get("/api/v1/pleroma/admin/email_list/combined.csv")
|> response(403)
end
end
end

View File

@ -1125,6 +1125,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
assert user assert user
assert user.is_confirmed assert user.is_confirmed
assert user.is_approved assert user.is_approved
refute user.accepts_email_list
end end
test "registers but does not log in with :account_activation_required", %{conn: conn} do test "registers but does not log in with :account_activation_required", %{conn: conn} do
@ -1356,6 +1357,20 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
assert json_response_and_validate_schema(res, 200) assert json_response_and_validate_schema(res, 200)
end end
test "registration with accepts_email_list", %{conn: conn, valid_params: valid_params} do
app_token = insert(:oauth_token, user: nil)
conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
res =
conn
|> put_req_header("content-type", "application/json")
|> Map.put(:remote_ip, {127, 0, 0, 9})
|> post("/api/v1/accounts", Map.put(valid_params, :accepts_email_list, true))
assert json_response_and_validate_schema(res, 200)
assert %User{accepts_email_list: true} = Repo.get_by(User, email: "lain@example.org")
end
test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
res = res =
conn conn

View File

@ -113,6 +113,13 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do
assert user_data["pleroma"]["accepts_chat_messages"] == false assert user_data["pleroma"]["accepts_chat_messages"] == false
end end
test "updates the user's newsletter preference", %{user: user, conn: conn} do
conn = patch(conn, "/api/v1/accounts/update_credentials", %{accepts_email_list: "true"})
assert json_response_and_validate_schema(conn, 200)
assert %User{accepts_email_list: true} = User.get_by_id(user.id)
end
test "updates the user's allow_following_move", %{user: user, conn: conn} do test "updates the user's allow_following_move", %{user: user, conn: conn} do
assert user.allow_following_move == true assert user.allow_following_move == true

View File

@ -487,6 +487,23 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
end end
end end
test "shows accepts_email_list only to the account owner" do
user = insert(:user)
other_user = insert(:user)
user = User.get_cached_by_ap_id(user.ap_id)
assert AccountView.render(
"show.json",
%{user: user, for: other_user}
)[:pleroma][:accepts_email_list] == nil
assert AccountView.render(
"show.json",
%{user: user, for: user}
)[:pleroma][:accepts_email_list] == user.accepts_email_list
end
describe "follow requests counter" do describe "follow requests counter" do
test "shows zero when no follow requests are pending" do test "shows zero when no follow requests are pending" do
user = insert(:user) user = insert(:user)

View File

@ -146,6 +146,20 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
assert user2.bio == expected_text assert user2.bio == expected_text
end end
test "it registers a new user with accepts_email_list." do
data = %{
:username => "lain",
:email => "lain@wired.jp",
:fullname => "lain iwakura",
:password => "bear",
:confirm => "bear",
:accepts_email_list => true
}
{:ok, user} = TwitterAPI.register_user(data)
assert user.accepts_email_list
end
describe "register with one time token" do describe "register with one time token" do
setup do: clear_config([:instance, :registrations_open], false) setup do: clear_config([:instance, :registrations_open], false)