summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Strizhakov <alex.strizhakov@gmail.com>2020-05-16 10:22:19 +0300
committerAlexander Strizhakov <alex.strizhakov@gmail.com>2020-05-18 13:03:21 +0300
commitfa7cfbd0b0ddc5c203f8eef56ce879397add4a0e (patch)
tree2754c145628db3ec3b77f31c57fca050d526c5be
parent1199cf3a788334cd3fdb968d9f736e43c1401da1 (diff)
adding domain mutes
-rw-r--r--lib/pleroma/notification.ex3
-rw-r--r--lib/pleroma/user.ex100
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex4
-rw-r--r--lib/pleroma/web/api_spec/operations/domain_mute_operation.ex81
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/views/status_view.ex8
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/domain_mute_controller.ex37
-rw-r--r--lib/pleroma/web/router.ex4
-rw-r--r--priv/repo/migrations/20200513112138_add_domain_mutes_to_users.exs9
-rw-r--r--test/web/mastodon_api/controllers/notification_controller_test.exs24
-rw-r--r--test/web/mastodon_api/controllers/timeline_controller_test.exs57
-rw-r--r--test/web/mastodon_api/views/status_view_test.exs3
-rw-r--r--test/web/pleroma_api/controllers/domain_mute_controller_test.exs44
13 files changed, 338 insertions, 38 deletions
diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index 8aa9ed2d4..8c7103bc3 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -120,8 +120,11 @@ defmodule Pleroma.Notification do
notification_muted_ap_ids =
opts[:notification_muted_users_ap_ids] || User.notification_muted_users_ap_ids(user)
+ domain_mutes = user.domain_mutes || []
+
query
|> where([n, a], a.actor not in ^notification_muted_ap_ids)
+ |> where([n, a], fragment("not split_part(?, '/', 3) = ANY(?)", a.actor, ^domain_mutes))
|> join(:left, [n, a], tm in ThreadMute,
on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data)
)
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index cba391072..6cd0995fa 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -30,6 +30,7 @@ defmodule Pleroma.User do
alias Pleroma.Web
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Builder
+ alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.ObjectValidators.Types
alias Pleroma.Web.ActivityPub.Pipeline
alias Pleroma.Web.ActivityPub.Utils
@@ -108,6 +109,7 @@ defmodule Pleroma.User do
field(:confirmation_token, :string, default: nil)
field(:default_scope, :string, default: "public")
field(:domain_blocks, {:array, :string}, default: [])
+ field(:domain_mutes, {:array, :string}, default: [])
field(:deactivated, :boolean, default: false)
field(:no_rich_text, :boolean, default: false)
field(:ap_enabled, :boolean, default: false)
@@ -359,8 +361,8 @@ defmodule Pleroma.User do
defp fix_follower_address(params), do: params
def remote_user_changeset(struct \\ %User{local: false}, params) do
- bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
- name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
+ bio_limit = Config.get([:instance, :user_bio_length], 5000)
+ name_limit = Config.get([:instance, :user_name_length], 100)
name =
case params[:name] do
@@ -419,8 +421,8 @@ defmodule Pleroma.User do
end
def update_changeset(struct, params \\ %{}) do
- bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
- name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
+ bio_limit = Config.get([:instance, :user_bio_length], 5000)
+ name_limit = Config.get([:instance, :user_name_length], 100)
struct
|> cast(
@@ -589,12 +591,12 @@ defmodule Pleroma.User do
def force_password_reset(user), do: update_password_reset_pending(user, true)
def register_changeset(struct, params \\ %{}, opts \\ []) do
- bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
- name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
+ bio_limit = Config.get([:instance, :user_bio_length], 5000)
+ name_limit = Config.get([:instance, :user_name_length], 100)
need_confirmation? =
if is_nil(opts[:need_confirmation]) do
- Pleroma.Config.get([:instance, :account_activation_required])
+ Config.get([:instance, :account_activation_required])
else
opts[:need_confirmation]
end
@@ -606,7 +608,7 @@ defmodule Pleroma.User do
|> validate_confirmation(:password)
|> unique_constraint(:email)
|> unique_constraint(:nickname)
- |> validate_exclusion(:nickname, Pleroma.Config.get([User, :restricted_nicknames]))
+ |> validate_exclusion(:nickname, Config.get([User, :restricted_nicknames]))
|> validate_format(:nickname, local_nickname_regex())
|> validate_format(:email, @email_regex)
|> validate_length(:bio, max: bio_limit)
@@ -621,7 +623,7 @@ defmodule Pleroma.User do
def maybe_validate_required_email(changeset, true), do: changeset
def maybe_validate_required_email(changeset, _) do
- if Pleroma.Config.get([:instance, :account_activation_required]) do
+ if Config.get([:instance, :account_activation_required]) do
validate_required(changeset, [:email])
else
changeset
@@ -641,7 +643,7 @@ defmodule Pleroma.User do
end
defp autofollow_users(user) do
- candidates = Pleroma.Config.get([:instance, :autofollowed_nicknames])
+ candidates = Config.get([:instance, :autofollowed_nicknames])
autofollowed_users =
User.Query.build(%{nickname: candidates, local: true, deactivated: false})
@@ -668,7 +670,7 @@ defmodule Pleroma.User do
def try_send_confirmation_email(%User{} = user) do
if user.confirmation_pending &&
- Pleroma.Config.get([:instance, :account_activation_required]) do
+ Config.get([:instance, :account_activation_required]) do
user
|> Pleroma.Emails.UserEmail.account_confirmation_email()
|> Pleroma.Emails.Mailer.deliver_async()
@@ -725,7 +727,7 @@ defmodule Pleroma.User do
defdelegate following(user), to: FollowingRelationship
def follow(%User{} = follower, %User{} = followed, state \\ :follow_accept) do
- deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
+ deny_follow_blocked = Config.get([:user, :deny_follow_blocked])
cond do
followed.deactivated ->
@@ -916,7 +918,7 @@ defmodule Pleroma.User do
end
def get_cached_by_nickname_or_id(nickname_or_id, opts \\ []) do
- restrict_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
+ restrict_to_local = Config.get([:instance, :limit_to_local_content])
cond do
is_integer(nickname_or_id) or FlakeId.flake_id?(nickname_or_id) ->
@@ -977,14 +979,14 @@ defmodule Pleroma.User do
@spec get_followers_query(User.t()) :: Ecto.Query.t()
def get_followers_query(user), do: get_followers_query(user, nil)
- @spec get_followers(User.t(), pos_integer() | nil) :: {:ok, list(User.t())}
+ @spec get_followers(User.t(), pos_integer() | nil) :: [User.t()]
def get_followers(user, page \\ nil) do
user
|> get_followers_query(page)
|> Repo.all()
end
- @spec get_external_followers(User.t(), pos_integer() | nil) :: {:ok, list(User.t())}
+ @spec get_external_followers(User.t(), pos_integer() | nil) :: [User.t()]
def get_external_followers(user, page \\ nil) do
user
|> get_followers_query(page)
@@ -1013,6 +1015,7 @@ defmodule Pleroma.User do
@spec get_friends_query(User.t()) :: Ecto.Query.t()
def get_friends_query(user), do: get_friends_query(user, nil)
+ @spec get_friends(User.t(), pos_integer() | nil) :: [User.t()]
def get_friends(user, page \\ nil) do
user
|> get_friends_query(page)
@@ -1111,7 +1114,7 @@ defmodule Pleroma.User do
end
def update_follower_count(%User{} = user) do
- if user.local or !Pleroma.Config.get([:instance, :external_user_synchronization]) do
+ if user.local or !Config.get([:instance, :external_user_synchronization]) do
follower_count_query =
User.Query.build(%{followers: user, deactivated: false})
|> select([u], %{count: count(u.id)})
@@ -1135,7 +1138,7 @@ defmodule Pleroma.User do
@spec update_following_count(User.t()) :: User.t()
def update_following_count(%User{local: false} = user) do
- if Pleroma.Config.get([:instance, :external_user_synchronization]) do
+ if Config.get([:instance, :external_user_synchronization]) do
maybe_fetch_follow_information(user)
else
user
@@ -1219,7 +1222,7 @@ defmodule Pleroma.User do
end
def subscribe(%User{} = subscriber, %User{} = target) do
- deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
+ deny_follow_blocked = Config.get([:user, :deny_follow_blocked])
if blocks?(target, subscriber) and deny_follow_blocked do
{:error, "Could not subscribe: #{target.nickname} is blocking you"}
@@ -1286,13 +1289,27 @@ defmodule Pleroma.User do
unblock(blocker, get_cached_by_ap_id(ap_id))
end
+ @spec mutes?(User.t() | nil, User.t()) :: boolean()
def mutes?(nil, _), do: false
- def mutes?(%User{} = user, %User{} = target), do: mutes_user?(user, target)
+ def mutes?(%User{} = user, %User{} = target) do
+ mutes_user?(user, target) || mutes_domain?(user, target)
+ end
+
+ @spec mutes_user?(User.t(), User.t()) :: boolean()
def mutes_user?(%User{} = user, %User{} = target) do
UserRelationship.mute_exists?(user, target)
end
+ @spec mutes_domain?(User.t() | nil, User.t()) :: boolean()
+ def mutes_domain?(nil, _), do: false
+
+ def mutes_domain?(%User{} = user, %User{} = target) do
+ domain_mutes = MRF.subdomains_regex(user.domain_mutes)
+ %{host: host} = URI.parse(target.ap_id)
+ MRF.subdomain_match?(domain_mutes, host)
+ end
+
@spec muted_notifications?(User.t() | nil, User.t() | map()) :: boolean()
def muted_notifications?(nil, _), do: false
@@ -1313,9 +1330,9 @@ defmodule Pleroma.User do
def blocks_user?(_, _), do: false
def blocks_domain?(%User{} = user, %User{} = target) do
- domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.domain_blocks)
+ domain_blocks = MRF.subdomains_regex(user.domain_blocks)
%{host: host} = URI.parse(target.ap_id)
- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, host)
+ MRF.subdomain_match?(domain_blocks, host)
end
def blocks_domain?(_, _), do: false
@@ -1584,7 +1601,7 @@ defmodule Pleroma.User do
Pleroma.HTML.Scrubber.TwitterText
end
- def html_filter_policy(_), do: Pleroma.Config.get([:markup, :scrub_policy])
+ def html_filter_policy(_), do: Config.get([:markup, :scrub_policy])
def fetch_by_ap_id(ap_id), do: ActivityPub.make_user_from_ap_id(ap_id)
@@ -1759,7 +1776,7 @@ defmodule Pleroma.User do
end
defp local_nickname_regex do
- if Pleroma.Config.get([:instance, :extended_nickname_format]) do
+ if Config.get([:instance, :extended_nickname_format]) do
@extended_local_nickname_regex
else
@strict_local_nickname_regex
@@ -1887,8 +1904,8 @@ defmodule Pleroma.User do
def get_mascot(%{mascot: mascot}) when is_nil(mascot) do
# use instance-default
- config = Pleroma.Config.get([:assets, :mascots])
- default_mascot = Pleroma.Config.get([:assets, :default_mascot])
+ config = Config.get([:assets, :mascots])
+ default_mascot = Config.get([:assets, :default_mascot])
mascot = Keyword.get(config, default_mascot)
%{
@@ -1983,7 +2000,7 @@ defmodule Pleroma.User do
def validate_fields(changeset, remote? \\ false) do
limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
- limit = Pleroma.Config.get([:instance, limit_name], 0)
+ limit = Config.get([:instance, limit_name], 0)
changeset
|> validate_length(:fields, max: limit)
@@ -1997,8 +2014,8 @@ defmodule Pleroma.User do
end
defp valid_field?(%{"name" => name, "value" => value}) do
- name_limit = Pleroma.Config.get([:instance, :account_field_name_length], 255)
- value_limit = Pleroma.Config.get([:instance, :account_field_value_length], 255)
+ name_limit = Config.get([:instance, :account_field_name_length], 255)
+ value_limit = Config.get([:instance, :account_field_value_length], 255)
is_binary(name) && is_binary(value) && String.length(name) <= name_limit &&
String.length(value) <= value_limit
@@ -2008,10 +2025,10 @@ defmodule Pleroma.User do
defp truncate_field(%{"name" => name, "value" => value}) do
{name, _chopped} =
- String.split_at(name, Pleroma.Config.get([:instance, :account_field_name_length], 255))
+ String.split_at(name, Config.get([:instance, :account_field_name_length], 255))
{value, _chopped} =
- String.split_at(value, Pleroma.Config.get([:instance, :account_field_value_length], 255))
+ String.split_at(value, Config.get([:instance, :account_field_value_length], 255))
%{"name" => name, "value" => value}
end
@@ -2066,7 +2083,7 @@ defmodule Pleroma.User do
def add_pinnned_activity(user, %Pleroma.Activity{id: id}) do
if id not in user.pinned_activities do
- max_pinned_statuses = Pleroma.Config.get([:instance, :max_pinned_statuses], 0)
+ max_pinned_statuses = Config.get([:instance, :max_pinned_statuses], 0)
params = %{pinned_activities: user.pinned_activities ++ [id]}
user
@@ -2121,13 +2138,32 @@ defmodule Pleroma.User do
set_domain_blocks(user, List.delete(user.domain_blocks, domain_blocked))
end
+ defp set_domain_mutes(user, domain_mutes) do
+ params = %{domain_mutes: domain_mutes}
+
+ user
+ |> cast(params, [:domain_mutes])
+ |> validate_required([:domain_mutes])
+ |> update_and_set_cache()
+ end
+
+ @spec mute_domain(User.t(), String.t()) :: {:ok, User.t()}
+ def mute_domain(%User{} = user, muted_domain) do
+ set_domain_mutes(user, Enum.uniq([muted_domain | user.domain_mutes]))
+ end
+
+ @spec unmute_domain(User.t(), String.t()) :: {:ok, User.t()}
+ def unmute_domain(user, muted_domain) do
+ set_domain_mutes(user, List.delete(user.domain_mutes, muted_domain))
+ end
+
@spec add_to_block(User.t(), User.t()) ::
{:ok, UserRelationship.t()} | {:error, Ecto.Changeset.t()}
defp add_to_block(%User{} = user, %User{} = blocked) do
UserRelationship.create_block(user, blocked)
end
- @spec add_to_block(User.t(), User.t()) ::
+ @spec remove_from_block(User.t(), User.t()) ::
{:ok, UserRelationship.t()} | {:ok, nil} | {:error, Ecto.Changeset.t()}
defp remove_from_block(%User{} = user, %User{} = blocked) do
UserRelationship.delete_block(user, blocked)
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index d752f4f04..d68cfba3f 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -924,11 +924,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
mutes = opts["muted_users_ap_ids"] || User.muted_users_ap_ids(user)
+ domain_mutes = user.domain_mutes || []
query =
from([activity] in query,
where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
- where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
+ where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes),
+ where: fragment("not split_part(?, '/', 3) = ANY(?)", activity.actor, ^domain_mutes)
)
unless opts["skip_preload"] do
diff --git a/lib/pleroma/web/api_spec/operations/domain_mute_operation.ex b/lib/pleroma/web/api_spec/operations/domain_mute_operation.ex
new file mode 100644
index 000000000..54d708c45
--- /dev/null
+++ b/lib/pleroma/web/api_spec/operations/domain_mute_operation.ex
@@ -0,0 +1,81 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.DomainMuteOperation do
+ alias OpenApiSpex.Operation
+ alias OpenApiSpex.Schema
+ import Pleroma.Web.ApiSpec.Helpers
+
+ def open_api_operation(action) do
+ operation = String.to_existing_atom("#{action}_operation")
+ apply(__MODULE__, operation, [])
+ end
+
+ def index_operation do
+ %Operation{
+ tags: ["domain_mutes"],
+ summary: "Fetch domain mutes",
+ description: "View domains the user has muted.",
+ security: [%{"oAuth" => ["follow", "read:mutes"]}],
+ operationId: "DomainMuteController.index",
+ responses: %{
+ 200 =>
+ Operation.response("Domain mutes", "application/json", %Schema{
+ description: "Response schema for domain mutes",
+ type: :array,
+ items: %Schema{type: :string},
+ example: ["google.com", "facebook.com"]
+ })
+ }
+ }
+ end
+
+ def create_operation do
+ %Operation{
+ tags: ["domain_mutes"],
+ summary: "Mute a domain",
+ description: """
+ Mute a domain to:
+
+ - hide all posts from it
+ - hide all notifications from it
+ """,
+ operationId: "DomainMuteController.create",
+ requestBody: domain_mute_request(),
+ security: [%{"oAuth" => ["follow", "write:mutes"]}],
+ responses: %{200 => empty_object_response()}
+ }
+ end
+
+ def delete_operation do
+ %Operation{
+ tags: ["domain_mutes"],
+ summary: "Unmute a domain",
+ description: "Remove a domain mute, if it exists in the user's array of muted domains.",
+ operationId: "DomainMuteController.delete",
+ requestBody: domain_mute_request(),
+ security: [%{"oAuth" => ["follow", "write:mutes"]}],
+ responses: %{
+ 200 => Operation.response("Empty object", "application/json", %Schema{type: :object})
+ }
+ }
+ end
+
+ defp domain_mute_request do
+ request_body(
+ "Parameters",
+ %Schema{
+ type: :object,
+ properties: %{
+ domain: %Schema{type: :string}
+ },
+ required: [:domain]
+ },
+ required: true,
+ example: %{
+ "domain" => "facebook.com"
+ }
+ )
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index 45fffaad2..04db1de84 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -109,7 +109,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
:mute,
reading_user,
target,
- &User.mutes?(&1, &2)
+ &User.mutes_user?(&1, &2)
),
muting_notifications:
UserRelationship.exists?(
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index 05a26017a..5622e6da8 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -308,6 +308,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
_ -> []
end
+ mutes_domain? = User.mutes_domain?(opts[:for], user)
# Status muted state (would do 1 request per status unless user mutes are preloaded)
muted =
thread_muted? ||
@@ -316,8 +317,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
:mute,
opts[:for],
user,
- fn for_user, user -> User.mutes?(for_user, user) end
- )
+ fn for_user, user -> User.mutes_user?(for_user, user) end
+ ) || mutes_domain?
%{
id: to_string(activity.id),
@@ -364,7 +365,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
expires_at: expires_at,
direct_conversation_id: direct_conversation_id,
thread_muted: thread_muted?,
- emoji_reactions: emoji_reactions
+ emoji_reactions: emoji_reactions,
+ instance_muted: mutes_domain?
}
}
end
diff --git a/lib/pleroma/web/pleroma_api/controllers/domain_mute_controller.ex b/lib/pleroma/web/pleroma_api/controllers/domain_mute_controller.ex
new file mode 100644
index 000000000..274094306
--- /dev/null
+++ b/lib/pleroma/web/pleroma_api/controllers/domain_mute_controller.ex
@@ -0,0 +1,37 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.DomainMuteController do
+ use Pleroma.Web, :controller
+
+ plug(Pleroma.Web.ApiSpec.CastAndValidate)
+ defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.DomainMuteOperation
+
+ plug(
+ Pleroma.Plugs.OAuthScopesPlug,
+ %{scopes: ["follow", "read:mutes"]} when action == :index
+ )
+
+ plug(
+ Pleroma.Plugs.OAuthScopesPlug,
+ %{scopes: ["follow", "write:mutes"]} when action != :index
+ )
+
+ @doc "GET /api/pleroma/domain_mutes"
+ def index(%{assigns: %{user: user}} = conn, _) do
+ json(conn, Map.get(user, :domain_mutes, []))
+ end
+
+ @doc "POST /api/pleroma/domain_mutes"
+ def create(%{assigns: %{user: user}, body_params: %{domain: domain}} = conn, _) do
+ Pleroma.User.mute_domain(user, domain)
+ json(conn, %{})
+ end
+
+ @doc "DELETE /api/pleroma/domain_mutes"
+ def delete(%{assigns: %{user: user}, body_params: %{domain: domain}} = conn, _) do
+ Pleroma.User.unmute_domain(user, domain)
+ json(conn, %{})
+ end
+end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index d77a61361..ce8187cff 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -267,6 +267,10 @@ defmodule Pleroma.Web.Router do
get("/accounts/mfa/setup/:method", TwoFactorAuthenticationController, :setup)
post("/accounts/mfa/confirm/:method", TwoFactorAuthenticationController, :confirm)
delete("/accounts/mfa/:method", TwoFactorAuthenticationController, :disable)
+
+ get("/domain_mutes", DomainMuteController, :index)
+ post("/domain_mutes", DomainMuteController, :create)
+ delete("/domain_mutes", DomainMuteController, :delete)
end
scope "/oauth", Pleroma.Web.OAuth do
diff --git a/priv/repo/migrations/20200513112138_add_domain_mutes_to_users.exs b/priv/repo/migrations/20200513112138_add_domain_mutes_to_users.exs
new file mode 100644
index 000000000..01b54d3fa
--- /dev/null
+++ b/priv/repo/migrations/20200513112138_add_domain_mutes_to_users.exs
@@ -0,0 +1,9 @@
+defmodule Pleroma.Repo.Migrations.AddDomainMutesToUsers do
+ use Ecto.Migration
+
+ def change do
+ alter table(:users) do
+ add_if_not_exists(:domain_mutes, {:array, :text}, default: [])
+ end
+ end
+end
diff --git a/test/web/mastodon_api/controllers/notification_controller_test.exs b/test/web/mastodon_api/controllers/notification_controller_test.exs
index 562fc4d8e..1912982ad 100644
--- a/test/web/mastodon_api/controllers/notification_controller_test.exs
+++ b/test/web/mastodon_api/controllers/notification_controller_test.exs
@@ -479,6 +479,30 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
assert length(json_response_and_validate_schema(conn, 200)) == 1
end
+ describe "muting domain" do
+ setup do
+ %{user: user, conn: conn} = oauth_access(["read:notifications"])
+ user2 = insert(:user, ap_id: "https://example.com/users/other_user")
+ {:ok, user, _, _} = CommonAPI.follow(user, user2)
+ {:ok, user} = User.mute_domain(user, "example.com")
+ {:ok, _} = CommonAPI.post(user2, %{status: "hello @#{user.nickname}"})
+ conn = assign(conn, :user, user)
+ [conn: conn]
+ end
+
+ test "see notifications with_muted parameter", %{conn: conn} do
+ conn = get(conn, "/api/v1/notifications?with_muted=true")
+
+ assert length(json_response_and_validate_schema(conn, 200)) == 1
+ end
+
+ test "doesn't see notifications without with_muted parameter", %{conn: conn} do
+ conn = get(conn, "/api/v1/notifications")
+
+ assert json_response_and_validate_schema(conn, 200) == []
+ end
+ end
+
@tag capture_log: true
test "see move notifications" do
old_user = insert(:user)
diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs
index 2375ac8e8..a0eaae795 100644
--- a/test/web/mastodon_api/controllers/timeline_controller_test.exs
+++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs
@@ -57,6 +57,63 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
end
end
+ describe "home timeline and user muting" do
+ setup do: oauth_access(["read:statuses"])
+
+ setup %{user: user} do
+ other_user = insert(:user, ap_id: "https://example.com/users/other_user")
+ {:ok, user} = User.follow(user, other_user)
+ {:ok, activity} = CommonAPI.post(other_user, %{status: "Some status"})
+
+ {:ok, _} = User.mute(user, other_user)
+ [activity: activity]
+ end
+
+ test "returns statuses with_muted = true", %{conn: conn, activity: activity} do
+ conn = get(conn, "/api/v1/timelines/home?with_muted=true")
+
+ [status] = json_response_and_validate_schema(conn, :ok)
+ assert activity.id == status["id"]
+ assert status["muted"]
+ refute status["pleroma"]["instance_muted"]
+ end
+
+ test "doesn't return statuses with_muted = false", %{conn: conn} do
+ conn = get(conn, "/api/v1/timelines/home?with_muted=false")
+
+ assert json_response_and_validate_schema(conn, :ok) == []
+ end
+ end
+
+ describe "home timeline and domain muting" do
+ setup do: oauth_access(["read:statuses"])
+
+ setup %{user: user, conn: conn} do
+ other_user = insert(:user, ap_id: "https://example.com/users/other_user")
+ {:ok, user} = User.follow(user, other_user)
+ {:ok, activity} = CommonAPI.post(other_user, %{status: "Some Status"})
+
+ {:ok, user} = User.mute_domain(user, "example.com")
+ conn = assign(conn, :user, user)
+ [activity: activity, conn: conn]
+ end
+
+ test "returns statuses with_muted = true", %{conn: conn, activity: activity} do
+ conn = get(conn, "/api/v1/timelines/home?with_muted=true")
+
+ [status] = json_response_and_validate_schema(conn, :ok)
+ assert activity.id == status["id"]
+ assert status["muted"]
+ assert status["pleroma"]["instance_muted"]
+ end
+
+ test "doesn't return statuses with_muted = false", %{conn: conn} do
+ conn = get(conn, "/api/v1/timelines/home?with_muted=false")
+
+ assert json_response_and_validate_schema(conn, :ok) == []
+ end
+ end
+
describe "public" do
@tag capture_log: true
test "the public timeline", %{conn: conn} do
diff --git a/test/web/mastodon_api/views/status_view_test.exs b/test/web/mastodon_api/views/status_view_test.exs
index 5d7adbe29..4787c9832 100644
--- a/test/web/mastodon_api/views/status_view_test.exs
+++ b/test/web/mastodon_api/views/status_view_test.exs
@@ -226,7 +226,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
expires_at: nil,
direct_conversation_id: nil,
thread_muted: false,
- emoji_reactions: []
+ emoji_reactions: [],
+ instance_muted: false
}
}
diff --git a/test/web/pleroma_api/controllers/domain_mute_controller_test.exs b/test/web/pleroma_api/controllers/domain_mute_controller_test.exs
new file mode 100644
index 000000000..79b71dfee
--- /dev/null
+++ b/test/web/pleroma_api/controllers/domain_mute_controller_test.exs
@@ -0,0 +1,44 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.DomainMuteControllerTest do
+ use Pleroma.Web.ConnCase, async: true
+
+ import Pleroma.Factory
+
+ alias Pleroma.User
+
+ test "muting / unmuting domain" do
+ %{user: user, conn: conn} = oauth_access(["write:mutes", "read:mutes"])
+ conn = put_req_header(conn, "content-type", "application/json")
+
+ other_user = insert(:user, ap_id: "https://example.com/users/other_user")
+
+ response = post(conn, "/api/pleroma/domain_mutes", %{"domain" => "example.com"})
+
+ assert %{} == json_response_and_validate_schema(response, 200)
+ user = User.get_cached_by_ap_id(user.ap_id)
+ assert User.mutes?(user, other_user)
+ assert User.mutes_domain?(user, other_user)
+
+ assert ["example.com"] ==
+ conn
+ |> assign(:user, user)
+ |> get("/api/pleroma/domain_mutes")
+ |> json_response_and_validate_schema(200)
+
+ response = delete(conn, "/api/pleroma/domain_mutes", %{"domain" => "example.com"})
+
+ assert %{} == json_response_and_validate_schema(response, 200)
+ user = User.get_cached_by_ap_id(user.ap_id)
+ refute User.mutes?(user, other_user)
+ refute User.mutes_domain?(user, other_user)
+
+ assert [] ==
+ conn
+ |> assign(:user, user)
+ |> get("/api/pleroma/domain_mutes")
+ |> json_response_and_validate_schema(200)
+ end
+end