forked from AkkomaGang/akkoma
Merge branch 'develop' into feature/moderation-log-filters
This commit is contained in:
commit
3542ca6702
46 changed files with 972 additions and 425 deletions
|
@ -11,16 +11,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
### Changed
|
### Changed
|
||||||
- **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config
|
- **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config
|
||||||
- **Breaking:** Configuration: `/media/` is now removed when `base_url` is configured, append `/media/` to your `base_url` config to keep the old behaviour if desired
|
- **Breaking:** Configuration: `/media/` is now removed when `base_url` is configured, append `/media/` to your `base_url` config to keep the old behaviour if desired
|
||||||
|
- **Breaking:** `/api/pleroma/notifications/read` is moved to `/api/v1/pleroma/notifications/read` and now supports `max_id` and responds with Mastodon API entities.
|
||||||
- Configuration: OpenGraph and TwitterCard providers enabled by default
|
- Configuration: OpenGraph and TwitterCard providers enabled by default
|
||||||
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
|
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
|
||||||
|
- Mastodon API: `pleroma.thread_muted` key in the Status entity
|
||||||
- Federation: Return 403 errors when trying to request pages from a user's follower/following collections if they have `hide_followers`/`hide_follows` set
|
- Federation: Return 403 errors when trying to request pages from a user's follower/following collections if they have `hide_followers`/`hide_follows` set
|
||||||
- NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
|
- NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
|
||||||
- NodeInfo: Return `mailerEnabled` in `metadata`
|
- NodeInfo: Return `mailerEnabled` in `metadata`
|
||||||
- Mastodon API: Unsubscribe followers when they unfollow a user
|
- Mastodon API: Unsubscribe followers when they unfollow a user
|
||||||
- AdminAPI: Add "godmode" while fetching user statuses (i.e. admin can see private statuses)
|
- AdminAPI: Add "godmode" while fetching user statuses (i.e. admin can see private statuses)
|
||||||
- Improve digest email template
|
- Improve digest email template
|
||||||
|
– Pagination: (optional) return `total` alongside with `items` when paginating
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
- Following from Osada
|
||||||
- Not being able to pin unlisted posts
|
- Not being able to pin unlisted posts
|
||||||
- Objects being re-embedded to activities after being updated (e.g faved/reposted). Running 'mix pleroma.database prune_objects' again is advised.
|
- Objects being re-embedded to activities after being updated (e.g faved/reposted). Running 'mix pleroma.database prune_objects' again is advised.
|
||||||
- Favorites timeline doing database-intensive queries
|
- Favorites timeline doing database-intensive queries
|
||||||
|
@ -48,6 +52,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Reverse Proxy limiting `max_body_length` was incorrectly defined and only checked `Content-Length` headers which may not be sufficient in some circumstances
|
- Reverse Proxy limiting `max_body_length` was incorrectly defined and only checked `Content-Length` headers which may not be sufficient in some circumstances
|
||||||
- MRF: fix use of unserializable keyword lists in describe() implementations
|
- MRF: fix use of unserializable keyword lists in describe() implementations
|
||||||
- ActivityPub: Deactivated user deletion
|
- ActivityPub: Deactivated user deletion
|
||||||
|
- MRF: fix ability to follow a relay when AntiFollowbotPolicy was enabled
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Expiring/ephemeral activites. All activities can have expires_at value set, which controls when they should be deleted automatically.
|
- Expiring/ephemeral activites. All activities can have expires_at value set, which controls when they should be deleted automatically.
|
||||||
|
|
|
@ -26,6 +26,7 @@ Has these additional fields under the `pleroma` object:
|
||||||
- `content`: a map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
|
- `content`: a map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
|
||||||
- `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
|
- `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
|
||||||
- `expires_at`: a datetime (iso8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire
|
- `expires_at`: a datetime (iso8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire
|
||||||
|
- `thread_muted`: true if the thread the post belongs to is muted
|
||||||
|
|
||||||
## Attachments
|
## Attachments
|
||||||
|
|
||||||
|
|
|
@ -126,13 +126,14 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi
|
||||||
## `/api/pleroma/admin/`…
|
## `/api/pleroma/admin/`…
|
||||||
See [Admin-API](Admin-API.md)
|
See [Admin-API](Admin-API.md)
|
||||||
|
|
||||||
## `/api/pleroma/notifications/read`
|
## `/api/v1/pleroma/notifications/read`
|
||||||
### Mark a single notification as read
|
### Mark notifications as read
|
||||||
* Method `POST`
|
* Method `POST`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
* Params:
|
* Params (mutually exclusive):
|
||||||
* `id`: notification's id
|
* `id`: a single notification id to read
|
||||||
* Response: JSON. Returns `{"status": "success"}` if the reading was successful, otherwise returns `{"error": "error_msg"}`
|
* `max_id`: read all notifications up to this id
|
||||||
|
* Response: Notification entity/Array of Notification entities that were read. In case of `max_id`, only the first 80 read notifications will be returned.
|
||||||
|
|
||||||
## `/api/v1/pleroma/accounts/:id/subscribe`
|
## `/api/v1/pleroma/accounts/:id/subscribe`
|
||||||
### Subscribe to receive notifications for all statuses posted by a user
|
### Subscribe to receive notifications for all statuses posted by a user
|
||||||
|
|
|
@ -71,26 +71,26 @@ server {
|
||||||
proxy_set_header Connection "upgrade";
|
proxy_set_header Connection "upgrade";
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $http_host;
|
||||||
|
|
||||||
# this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only
|
# this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only
|
||||||
# and `localhost.` resolves to [::0] on some systems: see issue #930
|
# and `localhost.` resolves to [::0] on some systems: see issue #930
|
||||||
proxy_pass http://127.0.0.1:4000;
|
proxy_pass http://127.0.0.1:4000;
|
||||||
|
|
||||||
client_max_body_size 16m;
|
client_max_body_size 16m;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~ ^/(media|proxy) {
|
location ~ ^/(media|proxy) {
|
||||||
proxy_cache pleroma_media_cache;
|
proxy_cache pleroma_media_cache;
|
||||||
slice 1m;
|
slice 1m;
|
||||||
proxy_cache_key $host$uri$is_args$args$slice_range;
|
proxy_cache_key $host$uri$is_args$args$slice_range;
|
||||||
proxy_set_header Range $slice_range;
|
proxy_set_header Range $slice_range;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_cache_valid 200 206 301 304 1h;
|
proxy_cache_valid 200 206 301 304 1h;
|
||||||
proxy_cache_lock on;
|
proxy_cache_lock on;
|
||||||
proxy_ignore_client_abort on;
|
proxy_ignore_client_abort on;
|
||||||
proxy_buffering on;
|
proxy_buffering on;
|
||||||
chunked_transfer_encoding on;
|
chunked_transfer_encoding on;
|
||||||
proxy_ignore_headers Cache-Control;
|
proxy_ignore_headers Cache-Control;
|
||||||
proxy_hide_header Cache-Control;
|
proxy_hide_header Cache-Control;
|
||||||
proxy_pass http://localhost:4000;
|
proxy_pass http://127.0.0.1:4000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
49
lib/pleroma/activity/queries.ex
Normal file
49
lib/pleroma/activity/queries.ex
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Activity.Queries do
|
||||||
|
@moduledoc """
|
||||||
|
Contains queries for Activity.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import Ecto.Query, only: [from: 2]
|
||||||
|
|
||||||
|
@type query :: Ecto.Queryable.t() | Activity.t()
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
|
||||||
|
@spec by_actor(query, String.t()) :: query
|
||||||
|
def by_actor(query \\ Activity, actor) do
|
||||||
|
from(
|
||||||
|
activity in query,
|
||||||
|
where: fragment("(?)->>'actor' = ?", activity.data, ^actor)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec by_object_id(query, String.t()) :: query
|
||||||
|
def by_object_id(query \\ Activity, object_id) do
|
||||||
|
from(activity in query,
|
||||||
|
where:
|
||||||
|
fragment(
|
||||||
|
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||||
|
activity.data,
|
||||||
|
activity.data,
|
||||||
|
^object_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec by_type(query, String.t()) :: query
|
||||||
|
def by_type(query \\ Activity, activity_type) do
|
||||||
|
from(
|
||||||
|
activity in query,
|
||||||
|
where: fragment("(?)->>'type' = ?", activity.data, ^activity_type)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec limit(query, pos_integer()) :: query
|
||||||
|
def limit(query \\ Activity, limit) do
|
||||||
|
from(activity in query, limit: ^limit)
|
||||||
|
end
|
||||||
|
end
|
|
@ -109,15 +109,19 @@ def rename(%Pleroma.List{} = list, title) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(title, %User{} = creator) do
|
def create(title, %User{} = creator) do
|
||||||
list = %Pleroma.List{user_id: creator.id, title: title}
|
changeset = title_changeset(%Pleroma.List{user_id: creator.id}, %{title: title})
|
||||||
|
|
||||||
Repo.transaction(fn ->
|
if changeset.valid? do
|
||||||
list = Repo.insert!(list)
|
Repo.transaction(fn ->
|
||||||
|
list = Repo.insert!(changeset)
|
||||||
|
|
||||||
list
|
list
|
||||||
|> change(ap_id: "#{creator.ap_id}/lists/#{list.id}")
|
|> change(ap_id: "#{creator.ap_id}/lists/#{list.id}")
|
||||||
|> Repo.update!()
|
|> Repo.update!()
|
||||||
end)
|
end)
|
||||||
|
else
|
||||||
|
{:error, changeset}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def follow(%Pleroma.List{following: following} = list, %User{} = followed) do
|
def follow(%Pleroma.List{following: following} = list, %User{} = followed) do
|
||||||
|
|
|
@ -102,15 +102,33 @@ def set_read_up_to(%{id: user_id} = _user, id) do
|
||||||
n in Notification,
|
n in Notification,
|
||||||
where: n.user_id == ^user_id,
|
where: n.user_id == ^user_id,
|
||||||
where: n.id <= ^id,
|
where: n.id <= ^id,
|
||||||
|
where: n.seen == false,
|
||||||
update: [
|
update: [
|
||||||
set: [
|
set: [
|
||||||
seen: true,
|
seen: true,
|
||||||
updated_at: ^NaiveDateTime.utc_now()
|
updated_at: ^NaiveDateTime.utc_now()
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
# Ideally we would preload object and activities here
|
||||||
|
# but Ecto does not support preloads in update_all
|
||||||
|
select: n.id
|
||||||
)
|
)
|
||||||
|
|
||||||
Repo.update_all(query, [])
|
{_, notification_ids} = Repo.update_all(query, [])
|
||||||
|
|
||||||
|
Notification
|
||||||
|
|> where([n], n.id in ^notification_ids)
|
||||||
|
|> join(:inner, [n], activity in assoc(n, :activity))
|
||||||
|
|> join(:left, [n, a], object in Object,
|
||||||
|
on:
|
||||||
|
fragment(
|
||||||
|
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
|
||||||
|
object.data,
|
||||||
|
a.data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> preload([n, a, o], activity: {a, object: o})
|
||||||
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_one(%User{} = user, notification_id) do
|
def read_one(%User{} = user, notification_id) do
|
||||||
|
|
|
@ -150,8 +150,6 @@ def set_cache(%Object{data: %{"id" => ap_id}} = object) do
|
||||||
def update_and_set_cache(changeset) do
|
def update_and_set_cache(changeset) do
|
||||||
with {:ok, object} <- Repo.update(changeset) do
|
with {:ok, object} <- Repo.update(changeset) do
|
||||||
set_cache(object)
|
set_cache(object)
|
||||||
else
|
|
||||||
e -> e
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,15 @@ defmodule Pleroma.Pagination do
|
||||||
|
|
||||||
def fetch_paginated(query, params, type \\ :keyset)
|
def fetch_paginated(query, params, type \\ :keyset)
|
||||||
|
|
||||||
|
def fetch_paginated(query, %{"total" => true} = params, :keyset) do
|
||||||
|
total = Repo.aggregate(query, :count, :id)
|
||||||
|
|
||||||
|
%{
|
||||||
|
total: total,
|
||||||
|
items: fetch_paginated(query, Map.drop(params, ["total"]), :keyset)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def fetch_paginated(query, params, :keyset) do
|
def fetch_paginated(query, params, :keyset) do
|
||||||
options = cast_params(params)
|
options = cast_params(params)
|
||||||
|
|
||||||
|
@ -25,6 +34,15 @@ def fetch_paginated(query, params, :keyset) do
|
||||||
|> enforce_order(options)
|
|> enforce_order(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fetch_paginated(query, %{"total" => true} = params, :offset) do
|
||||||
|
total = Repo.aggregate(query, :count, :id)
|
||||||
|
|
||||||
|
%{
|
||||||
|
total: total,
|
||||||
|
items: fetch_paginated(query, Map.drop(params, ["total"]), :offset)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def fetch_paginated(query, params, :offset) do
|
def fetch_paginated(query, params, :offset) do
|
||||||
options = cast_params(params)
|
options = cast_params(params)
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,7 @@ def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when
|
||||||
|
|
||||||
# Splice in the child object if we have one.
|
# Splice in the child object if we have one.
|
||||||
activity =
|
activity =
|
||||||
if !is_nil(object) do
|
if not is_nil(object) do
|
||||||
Map.put(activity, :object, object)
|
Map.put(activity, :object, object)
|
||||||
else
|
else
|
||||||
activity
|
activity
|
||||||
|
@ -331,12 +331,7 @@ def like(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def unlike(
|
def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
|
||||||
%User{} = actor,
|
|
||||||
%Object{} = object,
|
|
||||||
activity_id \\ nil,
|
|
||||||
local \\ true
|
|
||||||
) do
|
|
||||||
with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
|
with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
|
||||||
unlike_data <- make_unlike_data(actor, like_activity, activity_id),
|
unlike_data <- make_unlike_data(actor, like_activity, activity_id),
|
||||||
{:ok, unlike_activity} <- insert(unlike_data, local),
|
{:ok, unlike_activity} <- insert(unlike_data, local),
|
||||||
|
|
|
@ -309,42 +309,42 @@ def handle_user_activity(_, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_outbox(
|
def update_outbox(
|
||||||
%{assigns: %{user: user}} = conn,
|
%{assigns: %{user: %User{nickname: nickname} = user}} = conn,
|
||||||
%{"nickname" => nickname} = params
|
%{"nickname" => nickname} = params
|
||||||
) do
|
) do
|
||||||
if nickname == user.nickname do
|
actor = user.ap_id()
|
||||||
actor = user.ap_id()
|
|
||||||
|
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.drop(["id"])
|
|> Map.drop(["id"])
|
||||||
|> Map.put("actor", actor)
|
|> Map.put("actor", actor)
|
||||||
|> Transmogrifier.fix_addressing()
|
|> Transmogrifier.fix_addressing()
|
||||||
|
|
||||||
with {:ok, %Activity{} = activity} <- handle_user_activity(user, params) do
|
|
||||||
conn
|
|
||||||
|> put_status(:created)
|
|
||||||
|> put_resp_header("location", activity.data["id"])
|
|
||||||
|> json(activity.data)
|
|
||||||
else
|
|
||||||
{:error, message} ->
|
|
||||||
conn
|
|
||||||
|> put_status(:bad_request)
|
|
||||||
|> json(message)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
err =
|
|
||||||
dgettext("errors", "can't update outbox of %{nickname} as %{as_nickname}",
|
|
||||||
nickname: nickname,
|
|
||||||
as_nickname: user.nickname
|
|
||||||
)
|
|
||||||
|
|
||||||
|
with {:ok, %Activity{} = activity} <- handle_user_activity(user, params) do
|
||||||
conn
|
conn
|
||||||
|> put_status(:forbidden)
|
|> put_status(:created)
|
||||||
|> json(err)
|
|> put_resp_header("location", activity.data["id"])
|
||||||
|
|> json(activity.data)
|
||||||
|
else
|
||||||
|
{:error, message} ->
|
||||||
|
conn
|
||||||
|
|> put_status(:bad_request)
|
||||||
|
|> json(message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_outbox(%{assigns: %{user: user}} = conn, %{"nickname" => nickname} = _) do
|
||||||
|
err =
|
||||||
|
dgettext("errors", "can't update outbox of %{nickname} as %{as_nickname}",
|
||||||
|
nickname: nickname,
|
||||||
|
as_nickname: user.nickname
|
||||||
|
)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_status(:forbidden)
|
||||||
|
|> json(err)
|
||||||
|
end
|
||||||
|
|
||||||
def errors(conn, {:error, :not_found}) do
|
def errors(conn, {:error, :not_found}) do
|
||||||
conn
|
conn
|
||||||
|> put_status(:not_found)
|
|> put_status(:not_found)
|
||||||
|
|
|
@ -25,11 +25,15 @@ defp score_displayname("fedibot"), do: 1.0
|
||||||
defp score_displayname(_), do: 0.0
|
defp score_displayname(_), do: 0.0
|
||||||
|
|
||||||
defp determine_if_followbot(%User{nickname: nickname, name: displayname}) do
|
defp determine_if_followbot(%User{nickname: nickname, name: displayname}) do
|
||||||
# nickname will always be a binary string because it's generated by Pleroma.
|
# nickname will be a binary string except when following a relay
|
||||||
nick_score =
|
nick_score =
|
||||||
nickname
|
if is_binary(nickname) do
|
||||||
|> String.downcase()
|
nickname
|
||||||
|> score_nickname()
|
|> String.downcase()
|
||||||
|
|> score_nickname()
|
||||||
|
else
|
||||||
|
0.0
|
||||||
|
end
|
||||||
|
|
||||||
# displayname will either be a binary string or nil, if a displayname isn't set.
|
# displayname will either be a binary string or nil, if a displayname isn't set.
|
||||||
name_score =
|
name_score =
|
||||||
|
|
|
@ -464,8 +464,10 @@ def handle_incoming(
|
||||||
%{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data,
|
%{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data,
|
||||||
_options
|
_options
|
||||||
) do
|
) do
|
||||||
with %User{local: true} = followed <- User.get_cached_by_ap_id(followed),
|
with %User{local: true} = followed <-
|
||||||
{:ok, %User{} = follower} <- User.get_or_fetch_by_ap_id(follower),
|
User.get_cached_by_ap_id(Containment.get_actor(%{"actor" => followed})),
|
||||||
|
{:ok, %User{} = follower} <-
|
||||||
|
User.get_or_fetch_by_ap_id(Containment.get_actor(%{"actor" => follower})),
|
||||||
{:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
|
{:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
|
||||||
with deny_follow_blocked <- Pleroma.Config.get([:user, :deny_follow_blocked]),
|
with deny_follow_blocked <- Pleroma.Config.get([:user, :deny_follow_blocked]),
|
||||||
{_, false} <- {:user_blocked, User.blocks?(followed, follower) && deny_follow_blocked},
|
{_, false} <- {:user_blocked, User.blocks?(followed, follower) && deny_follow_blocked},
|
||||||
|
|
|
@ -166,6 +166,7 @@ def create_context(context) do
|
||||||
@doc """
|
@doc """
|
||||||
Enqueues an activity for federation if it's local
|
Enqueues an activity for federation if it's local
|
||||||
"""
|
"""
|
||||||
|
@spec maybe_federate(any()) :: :ok
|
||||||
def maybe_federate(%Activity{local: true} = activity) do
|
def maybe_federate(%Activity{local: true} = activity) do
|
||||||
if Pleroma.Config.get!([:instance, :federating]) do
|
if Pleroma.Config.get!([:instance, :federating]) do
|
||||||
priority =
|
priority =
|
||||||
|
@ -256,46 +257,27 @@ def insert_full_object(map), do: {:ok, map, nil}
|
||||||
@doc """
|
@doc """
|
||||||
Returns an existing like if a user already liked an object
|
Returns an existing like if a user already liked an object
|
||||||
"""
|
"""
|
||||||
|
@spec get_existing_like(String.t(), map()) :: Activity.t() | nil
|
||||||
def get_existing_like(actor, %{data: %{"id" => id}}) do
|
def get_existing_like(actor, %{data: %{"id" => id}}) do
|
||||||
query =
|
actor
|
||||||
from(
|
|> Activity.Queries.by_actor()
|
||||||
activity in Activity,
|
|> Activity.Queries.by_object_id(id)
|
||||||
where: fragment("(?)->>'actor' = ?", activity.data, ^actor),
|
|> Activity.Queries.by_type("Like")
|
||||||
# this is to use the index
|
|> Activity.Queries.limit(1)
|
||||||
where:
|
|> Repo.one()
|
||||||
fragment(
|
|
||||||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
|
||||||
activity.data,
|
|
||||||
activity.data,
|
|
||||||
^id
|
|
||||||
),
|
|
||||||
where: fragment("(?)->>'type' = 'Like'", activity.data)
|
|
||||||
)
|
|
||||||
|
|
||||||
Repo.one(query)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns like activities targeting an object
|
Returns like activities targeting an object
|
||||||
"""
|
"""
|
||||||
def get_object_likes(%{data: %{"id" => id}}) do
|
def get_object_likes(%{data: %{"id" => id}}) do
|
||||||
query =
|
id
|
||||||
from(
|
|> Activity.Queries.by_object_id()
|
||||||
activity in Activity,
|
|> Activity.Queries.by_type("Like")
|
||||||
# this is to use the index
|
|> Repo.all()
|
||||||
where:
|
|
||||||
fragment(
|
|
||||||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
|
||||||
activity.data,
|
|
||||||
activity.data,
|
|
||||||
^id
|
|
||||||
),
|
|
||||||
where: fragment("(?)->>'type' = 'Like'", activity.data)
|
|
||||||
)
|
|
||||||
|
|
||||||
Repo.all(query)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec make_like_data(User.t(), map(), String.t()) :: map()
|
||||||
def make_like_data(
|
def make_like_data(
|
||||||
%User{ap_id: ap_id} = actor,
|
%User{ap_id: ap_id} = actor,
|
||||||
%{data: %{"actor" => object_actor_id, "id" => id}} = object,
|
%{data: %{"actor" => object_actor_id, "id" => id}} = object,
|
||||||
|
@ -315,7 +297,7 @@ def make_like_data(
|
||||||
|> List.delete(actor.ap_id)
|
|> List.delete(actor.ap_id)
|
||||||
|> List.delete(object_actor.follower_address)
|
|> List.delete(object_actor.follower_address)
|
||||||
|
|
||||||
data = %{
|
%{
|
||||||
"type" => "Like",
|
"type" => "Like",
|
||||||
"actor" => ap_id,
|
"actor" => ap_id,
|
||||||
"object" => id,
|
"object" => id,
|
||||||
|
@ -323,38 +305,49 @@ def make_like_data(
|
||||||
"cc" => cc,
|
"cc" => cc,
|
||||||
"context" => object.data["context"]
|
"context" => object.data["context"]
|
||||||
}
|
}
|
||||||
|
|> maybe_put("id", activity_id)
|
||||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec update_element_in_object(String.t(), list(any), Object.t()) ::
|
||||||
|
{:ok, Object.t()} | {:error, Ecto.Changeset.t()}
|
||||||
def update_element_in_object(property, element, object) do
|
def update_element_in_object(property, element, object) do
|
||||||
with new_data <-
|
data =
|
||||||
object.data
|
Map.merge(
|
||||||
|> Map.put("#{property}_count", length(element))
|
object.data,
|
||||||
|> Map.put("#{property}s", element),
|
%{"#{property}_count" => length(element), "#{property}s" => element}
|
||||||
changeset <- Changeset.change(object, data: new_data),
|
)
|
||||||
{:ok, object} <- Object.update_and_set_cache(changeset) do
|
|
||||||
{:ok, object}
|
object
|
||||||
end
|
|> Changeset.change(data: data)
|
||||||
|
|> Object.update_and_set_cache()
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_likes_in_object(likes, object) do
|
@spec add_like_to_object(Activity.t(), Object.t()) ::
|
||||||
|
{:ok, Object.t()} | {:error, Ecto.Changeset.t()}
|
||||||
|
def add_like_to_object(%Activity{data: %{"actor" => actor}}, object) do
|
||||||
|
[actor | fetch_likes(object)]
|
||||||
|
|> Enum.uniq()
|
||||||
|
|> update_likes_in_object(object)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec remove_like_from_object(Activity.t(), Object.t()) ::
|
||||||
|
{:ok, Object.t()} | {:error, Ecto.Changeset.t()}
|
||||||
|
def remove_like_from_object(%Activity{data: %{"actor" => actor}}, object) do
|
||||||
|
object
|
||||||
|
|> fetch_likes()
|
||||||
|
|> List.delete(actor)
|
||||||
|
|> update_likes_in_object(object)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_likes_in_object(likes, object) do
|
||||||
update_element_in_object("like", likes, object)
|
update_element_in_object("like", likes, object)
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_like_to_object(%Activity{data: %{"actor" => actor}}, object) do
|
defp fetch_likes(object) do
|
||||||
likes = if is_list(object.data["likes"]), do: object.data["likes"], else: []
|
if is_list(object.data["likes"]) do
|
||||||
|
object.data["likes"]
|
||||||
with likes <- [actor | likes] |> Enum.uniq() do
|
else
|
||||||
update_likes_in_object(likes, object)
|
[]
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_like_from_object(%Activity{data: %{"actor" => actor}}, object) do
|
|
||||||
likes = if is_list(object.data["likes"]), do: object.data["likes"], else: []
|
|
||||||
|
|
||||||
with likes <- likes |> List.delete(actor) do
|
|
||||||
update_likes_in_object(likes, object)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -405,7 +398,7 @@ def make_follow_data(
|
||||||
%User{ap_id: followed_id} = _followed,
|
%User{ap_id: followed_id} = _followed,
|
||||||
activity_id
|
activity_id
|
||||||
) do
|
) do
|
||||||
data = %{
|
%{
|
||||||
"type" => "Follow",
|
"type" => "Follow",
|
||||||
"actor" => follower_id,
|
"actor" => follower_id,
|
||||||
"to" => [followed_id],
|
"to" => [followed_id],
|
||||||
|
@ -413,10 +406,7 @@ def make_follow_data(
|
||||||
"object" => followed_id,
|
"object" => followed_id,
|
||||||
"state" => "pending"
|
"state" => "pending"
|
||||||
}
|
}
|
||||||
|
|> maybe_put("id", activity_id)
|
||||||
data = if activity_id, do: Map.put(data, "id", activity_id), else: data
|
|
||||||
|
|
||||||
data
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do
|
def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do
|
||||||
|
@ -478,7 +468,7 @@ def make_announce_data(
|
||||||
activity_id,
|
activity_id,
|
||||||
false
|
false
|
||||||
) do
|
) do
|
||||||
data = %{
|
%{
|
||||||
"type" => "Announce",
|
"type" => "Announce",
|
||||||
"actor" => ap_id,
|
"actor" => ap_id,
|
||||||
"object" => id,
|
"object" => id,
|
||||||
|
@ -486,8 +476,7 @@ def make_announce_data(
|
||||||
"cc" => [],
|
"cc" => [],
|
||||||
"context" => object.data["context"]
|
"context" => object.data["context"]
|
||||||
}
|
}
|
||||||
|
|> maybe_put("id", activity_id)
|
||||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_announce_data(
|
def make_announce_data(
|
||||||
|
@ -496,7 +485,7 @@ def make_announce_data(
|
||||||
activity_id,
|
activity_id,
|
||||||
true
|
true
|
||||||
) do
|
) do
|
||||||
data = %{
|
%{
|
||||||
"type" => "Announce",
|
"type" => "Announce",
|
||||||
"actor" => ap_id,
|
"actor" => ap_id,
|
||||||
"object" => id,
|
"object" => id,
|
||||||
|
@ -504,8 +493,7 @@ def make_announce_data(
|
||||||
"cc" => [Pleroma.Constants.as_public()],
|
"cc" => [Pleroma.Constants.as_public()],
|
||||||
"context" => object.data["context"]
|
"context" => object.data["context"]
|
||||||
}
|
}
|
||||||
|
|> maybe_put("id", activity_id)
|
||||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -516,7 +504,7 @@ def make_unannounce_data(
|
||||||
%Activity{data: %{"context" => context}} = activity,
|
%Activity{data: %{"context" => context}} = activity,
|
||||||
activity_id
|
activity_id
|
||||||
) do
|
) do
|
||||||
data = %{
|
%{
|
||||||
"type" => "Undo",
|
"type" => "Undo",
|
||||||
"actor" => ap_id,
|
"actor" => ap_id,
|
||||||
"object" => activity.data,
|
"object" => activity.data,
|
||||||
|
@ -524,8 +512,7 @@ def make_unannounce_data(
|
||||||
"cc" => [Pleroma.Constants.as_public()],
|
"cc" => [Pleroma.Constants.as_public()],
|
||||||
"context" => context
|
"context" => context
|
||||||
}
|
}
|
||||||
|
|> maybe_put("id", activity_id)
|
||||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_unlike_data(
|
def make_unlike_data(
|
||||||
|
@ -533,7 +520,7 @@ def make_unlike_data(
|
||||||
%Activity{data: %{"context" => context}} = activity,
|
%Activity{data: %{"context" => context}} = activity,
|
||||||
activity_id
|
activity_id
|
||||||
) do
|
) do
|
||||||
data = %{
|
%{
|
||||||
"type" => "Undo",
|
"type" => "Undo",
|
||||||
"actor" => ap_id,
|
"actor" => ap_id,
|
||||||
"object" => activity.data,
|
"object" => activity.data,
|
||||||
|
@ -541,8 +528,7 @@ def make_unlike_data(
|
||||||
"cc" => [Pleroma.Constants.as_public()],
|
"cc" => [Pleroma.Constants.as_public()],
|
||||||
"context" => context
|
"context" => context
|
||||||
}
|
}
|
||||||
|
|> maybe_put("id", activity_id)
|
||||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_announce_to_object(
|
def add_announce_to_object(
|
||||||
|
@ -573,14 +559,13 @@ def remove_announce_from_object(%Activity{data: %{"actor" => actor}}, object) do
|
||||||
#### Unfollow-related helpers
|
#### Unfollow-related helpers
|
||||||
|
|
||||||
def make_unfollow_data(follower, followed, follow_activity, activity_id) do
|
def make_unfollow_data(follower, followed, follow_activity, activity_id) do
|
||||||
data = %{
|
%{
|
||||||
"type" => "Undo",
|
"type" => "Undo",
|
||||||
"actor" => follower.ap_id,
|
"actor" => follower.ap_id,
|
||||||
"to" => [followed.ap_id],
|
"to" => [followed.ap_id],
|
||||||
"object" => follow_activity.data
|
"object" => follow_activity.data
|
||||||
}
|
}
|
||||||
|
|> maybe_put("id", activity_id)
|
||||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#### Block-related helpers
|
#### Block-related helpers
|
||||||
|
@ -610,25 +595,23 @@ def fetch_latest_block(%User{ap_id: blocker_id}, %User{ap_id: blocked_id}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_block_data(blocker, blocked, activity_id) do
|
def make_block_data(blocker, blocked, activity_id) do
|
||||||
data = %{
|
%{
|
||||||
"type" => "Block",
|
"type" => "Block",
|
||||||
"actor" => blocker.ap_id,
|
"actor" => blocker.ap_id,
|
||||||
"to" => [blocked.ap_id],
|
"to" => [blocked.ap_id],
|
||||||
"object" => blocked.ap_id
|
"object" => blocked.ap_id
|
||||||
}
|
}
|
||||||
|
|> maybe_put("id", activity_id)
|
||||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_unblock_data(blocker, blocked, block_activity, activity_id) do
|
def make_unblock_data(blocker, blocked, block_activity, activity_id) do
|
||||||
data = %{
|
%{
|
||||||
"type" => "Undo",
|
"type" => "Undo",
|
||||||
"actor" => blocker.ap_id,
|
"actor" => blocker.ap_id,
|
||||||
"to" => [blocked.ap_id],
|
"to" => [blocked.ap_id],
|
||||||
"object" => block_activity.data
|
"object" => block_activity.data
|
||||||
}
|
}
|
||||||
|
|> maybe_put("id", activity_id)
|
||||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#### Create-related helpers
|
#### Create-related helpers
|
||||||
|
@ -799,4 +782,7 @@ def get_existing_votes(actor, %{data: %{"id" => id}}) do
|
||||||
|
|
||||||
Repo.all(query)
|
Repo.all(query)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp maybe_put(map, _key, nil), do: map
|
||||||
|
defp maybe_put(map, key, value), do: Map.put(map, key, value)
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.FallbackController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
|
||||||
|
error_message =
|
||||||
|
changeset
|
||||||
|
|> Ecto.Changeset.traverse_errors(fn {message, _opt} -> message end)
|
||||||
|
|> Enum.map_join(", ", fn {_k, v} -> v end)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_status(:unprocessable_entity)
|
||||||
|
|> json(%{error: error_message})
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(conn, {:error, :not_found}) do
|
||||||
|
render_error(conn, :not_found, "Record not found")
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(conn, {:error, error_message}) do
|
||||||
|
conn
|
||||||
|
|> put_status(:bad_request)
|
||||||
|
|> json(%{error: error_message})
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(conn, _) do
|
||||||
|
conn
|
||||||
|
|> put_status(:internal_server_error)
|
||||||
|
|> json(dgettext("errors", "Something went wrong"))
|
||||||
|
end
|
||||||
|
end
|
84
lib/pleroma/web/mastodon_api/controllers/list_controller.ex
Normal file
84
lib/pleroma/web/mastodon_api/controllers/list_controller.ex
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.ListController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
|
|
||||||
|
plug(:list_by_id_and_user when action not in [:index, :create])
|
||||||
|
|
||||||
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
|
# GET /api/v1/lists
|
||||||
|
def index(%{assigns: %{user: user}} = conn, opts) do
|
||||||
|
lists = Pleroma.List.for_user(user, opts)
|
||||||
|
render(conn, "index.json", lists: lists)
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /api/v1/lists
|
||||||
|
def create(%{assigns: %{user: user}} = conn, %{"title" => title}) do
|
||||||
|
with {:ok, %Pleroma.List{} = list} <- Pleroma.List.create(title, user) do
|
||||||
|
render(conn, "show.json", list: list)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /api/v1/lists/:id
|
||||||
|
def show(%{assigns: %{list: list}} = conn, _) do
|
||||||
|
render(conn, "show.json", list: list)
|
||||||
|
end
|
||||||
|
|
||||||
|
# PUT /api/v1/lists/:id
|
||||||
|
def update(%{assigns: %{list: list}} = conn, %{"title" => title}) do
|
||||||
|
with {:ok, list} <- Pleroma.List.rename(list, title) do
|
||||||
|
render(conn, "show.json", list: list)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# DELETE /api/v1/lists/:id
|
||||||
|
def delete(%{assigns: %{list: list}} = conn, _) do
|
||||||
|
with {:ok, _list} <- Pleroma.List.delete(list) do
|
||||||
|
json(conn, %{})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /api/v1/lists/:id/accounts
|
||||||
|
def list_accounts(%{assigns: %{user: user, list: list}} = conn, _) do
|
||||||
|
with {:ok, users} <- Pleroma.List.get_following(list) do
|
||||||
|
conn
|
||||||
|
|> put_view(AccountView)
|
||||||
|
|> render("accounts.json", for: user, users: users, as: :user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /api/v1/lists/:id/accounts
|
||||||
|
def add_to_list(%{assigns: %{list: list}} = conn, %{"account_ids" => account_ids}) do
|
||||||
|
Enum.each(account_ids, fn account_id ->
|
||||||
|
with %User{} = followed <- User.get_cached_by_id(account_id) do
|
||||||
|
Pleroma.List.follow(list, followed)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
json(conn, %{})
|
||||||
|
end
|
||||||
|
|
||||||
|
# DELETE /api/v1/lists/:id/accounts
|
||||||
|
def remove_from_list(%{assigns: %{list: list}} = conn, %{"account_ids" => account_ids}) do
|
||||||
|
Enum.each(account_ids, fn account_id ->
|
||||||
|
with %User{} = followed <- User.get_cached_by_id(account_id) do
|
||||||
|
Pleroma.List.unfollow(list, followed)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
json(conn, %{})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp list_by_id_and_user(%{assigns: %{user: user}, params: %{"id" => id}} = conn, _) do
|
||||||
|
case Pleroma.List.get(id, user) do
|
||||||
|
%Pleroma.List{} = list -> assign(conn, :list, list)
|
||||||
|
nil -> conn |> render_error(:not_found, "List not found") |> halt()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -83,7 +83,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
|
|
||||||
@local_mastodon_name "Mastodon-Local"
|
@local_mastodon_name "Mastodon-Local"
|
||||||
|
|
||||||
action_fallback(:errors)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
def create_app(conn, params) do
|
def create_app(conn, params) do
|
||||||
scopes = Scopes.fetch_scopes(params, ["read"])
|
scopes = Scopes.fetch_scopes(params, ["read"])
|
||||||
|
@ -189,7 +189,7 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
||||||
info_cng = User.Info.profile_update(user.info, info_params)
|
info_cng = User.Info.profile_update(user.info, info_params)
|
||||||
|
|
||||||
with changeset <- User.update_changeset(user, user_params),
|
with changeset <- User.update_changeset(user, user_params),
|
||||||
changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng),
|
changeset <- Changeset.put_embed(changeset, :info, info_cng),
|
||||||
{:ok, user} <- User.update_and_set_cache(changeset) do
|
{:ok, user} <- User.update_and_set_cache(changeset) do
|
||||||
if original_user != user do
|
if original_user != user do
|
||||||
CommonAPI.update(user)
|
CommonAPI.update(user)
|
||||||
|
@ -225,7 +225,7 @@ def update_avatar(%{assigns: %{user: user}} = conn, params) do
|
||||||
def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
|
def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
|
||||||
with new_info <- %{"banner" => %{}},
|
with new_info <- %{"banner" => %{}},
|
||||||
info_cng <- User.Info.profile_update(user.info, new_info),
|
info_cng <- User.Info.profile_update(user.info, new_info),
|
||||||
changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
|
changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_cng),
|
||||||
{:ok, user} <- User.update_and_set_cache(changeset) do
|
{:ok, user} <- User.update_and_set_cache(changeset) do
|
||||||
CommonAPI.update(user)
|
CommonAPI.update(user)
|
||||||
|
|
||||||
|
@ -237,7 +237,7 @@ def update_banner(%{assigns: %{user: user}} = conn, params) do
|
||||||
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
|
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
|
||||||
new_info <- %{"banner" => object.data},
|
new_info <- %{"banner" => object.data},
|
||||||
info_cng <- User.Info.profile_update(user.info, new_info),
|
info_cng <- User.Info.profile_update(user.info, new_info),
|
||||||
changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
|
changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_cng),
|
||||||
{:ok, user} <- User.update_and_set_cache(changeset) do
|
{:ok, user} <- User.update_and_set_cache(changeset) do
|
||||||
CommonAPI.update(user)
|
CommonAPI.update(user)
|
||||||
%{"url" => [%{"href" => href} | _]} = object.data
|
%{"url" => [%{"href" => href} | _]} = object.data
|
||||||
|
@ -249,7 +249,7 @@ def update_banner(%{assigns: %{user: user}} = conn, params) do
|
||||||
def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
|
def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
|
||||||
with new_info <- %{"background" => %{}},
|
with new_info <- %{"background" => %{}},
|
||||||
info_cng <- User.Info.profile_update(user.info, new_info),
|
info_cng <- User.Info.profile_update(user.info, new_info),
|
||||||
changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
|
changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_cng),
|
||||||
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
||||||
json(conn, %{url: nil})
|
json(conn, %{url: nil})
|
||||||
end
|
end
|
||||||
|
@ -259,7 +259,7 @@ def update_background(%{assigns: %{user: user}} = conn, params) do
|
||||||
with {:ok, object} <- ActivityPub.upload(params, type: :background),
|
with {:ok, object} <- ActivityPub.upload(params, type: :background),
|
||||||
new_info <- %{"background" => object.data},
|
new_info <- %{"background" => object.data},
|
||||||
info_cng <- User.Info.profile_update(user.info, new_info),
|
info_cng <- User.Info.profile_update(user.info, new_info),
|
||||||
changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
|
changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_cng),
|
||||||
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
||||||
%{"url" => [%{"href" => href} | _]} = object.data
|
%{"url" => [%{"href" => href} | _]} = object.data
|
||||||
|
|
||||||
|
@ -806,8 +806,8 @@ def set_mascot(%{assigns: %{user: user}} = conn, %{"file" => file}) do
|
||||||
|
|
||||||
user_changeset =
|
user_changeset =
|
||||||
user
|
user
|
||||||
|> Ecto.Changeset.change()
|
|> Changeset.change()
|
||||||
|> Ecto.Changeset.put_embed(:info, info_changeset)
|
|> Changeset.put_embed(:info, info_changeset)
|
||||||
|
|
||||||
{:ok, _user} = User.update_and_set_cache(user_changeset)
|
{:ok, _user} = User.update_and_set_cache(user_changeset)
|
||||||
|
|
||||||
|
@ -1205,88 +1205,12 @@ def bookmarks(%{assigns: %{user: user}} = conn, params) do
|
||||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_lists(%{assigns: %{user: user}} = conn, opts) do
|
|
||||||
lists = Pleroma.List.for_user(user, opts)
|
|
||||||
res = ListView.render("lists.json", lists: lists)
|
|
||||||
json(conn, res)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_list(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
|
||||||
with %Pleroma.List{} = list <- Pleroma.List.get(id, user) do
|
|
||||||
res = ListView.render("list.json", list: list)
|
|
||||||
json(conn, res)
|
|
||||||
else
|
|
||||||
_e -> render_error(conn, :not_found, "Record not found")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def account_lists(%{assigns: %{user: user}} = conn, %{"id" => account_id}) do
|
def account_lists(%{assigns: %{user: user}} = conn, %{"id" => account_id}) do
|
||||||
lists = Pleroma.List.get_lists_account_belongs(user, account_id)
|
lists = Pleroma.List.get_lists_account_belongs(user, account_id)
|
||||||
res = ListView.render("lists.json", lists: lists)
|
res = ListView.render("lists.json", lists: lists)
|
||||||
json(conn, res)
|
json(conn, res)
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_list(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
|
||||||
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
|
|
||||||
{:ok, _list} <- Pleroma.List.delete(list) do
|
|
||||||
json(conn, %{})
|
|
||||||
else
|
|
||||||
_e ->
|
|
||||||
json(conn, dgettext("errors", "error"))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_list(%{assigns: %{user: user}} = conn, %{"title" => title}) do
|
|
||||||
with {:ok, %Pleroma.List{} = list} <- Pleroma.List.create(title, user) do
|
|
||||||
res = ListView.render("list.json", list: list)
|
|
||||||
json(conn, res)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_to_list(%{assigns: %{user: user}} = conn, %{"id" => id, "account_ids" => accounts}) do
|
|
||||||
accounts
|
|
||||||
|> Enum.each(fn account_id ->
|
|
||||||
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
|
|
||||||
%User{} = followed <- User.get_cached_by_id(account_id) do
|
|
||||||
Pleroma.List.follow(list, followed)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
json(conn, %{})
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_from_list(%{assigns: %{user: user}} = conn, %{"id" => id, "account_ids" => accounts}) do
|
|
||||||
accounts
|
|
||||||
|> Enum.each(fn account_id ->
|
|
||||||
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
|
|
||||||
%User{} = followed <- User.get_cached_by_id(account_id) do
|
|
||||||
Pleroma.List.unfollow(list, followed)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
json(conn, %{})
|
|
||||||
end
|
|
||||||
|
|
||||||
def list_accounts(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
|
||||||
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
|
|
||||||
{:ok, users} = Pleroma.List.get_following(list) do
|
|
||||||
conn
|
|
||||||
|> put_view(AccountView)
|
|
||||||
|> render("accounts.json", %{for: user, users: users, as: :user})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def rename_list(%{assigns: %{user: user}} = conn, %{"id" => id, "title" => title}) do
|
|
||||||
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
|
|
||||||
{:ok, list} <- Pleroma.List.rename(list, title) do
|
|
||||||
res = ListView.render("list.json", list: list)
|
|
||||||
json(conn, res)
|
|
||||||
else
|
|
||||||
_e ->
|
|
||||||
json(conn, dgettext("errors", "error"))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do
|
def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do
|
||||||
with %Pleroma.List{title: _title, following: following} <- Pleroma.List.get(id, user) do
|
with %Pleroma.List{title: _title, following: following} <- Pleroma.List.get(id, user) do
|
||||||
params =
|
params =
|
||||||
|
@ -1420,8 +1344,8 @@ def index(%{assigns: %{user: user}} = conn, _params) do
|
||||||
def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
|
def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
|
||||||
info_cng = User.Info.mastodon_settings_update(user.info, settings)
|
info_cng = User.Info.mastodon_settings_update(user.info, settings)
|
||||||
|
|
||||||
with changeset <- Ecto.Changeset.change(user),
|
with changeset <- Changeset.change(user),
|
||||||
changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng),
|
changeset <- Changeset.put_embed(changeset, :info, info_cng),
|
||||||
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
||||||
json(conn, %{})
|
json(conn, %{})
|
||||||
else
|
else
|
||||||
|
@ -1485,7 +1409,7 @@ defp get_or_make_app do
|
||||||
{:ok, app}
|
{:ok, app}
|
||||||
else
|
else
|
||||||
app
|
app
|
||||||
|> Ecto.Changeset.change(%{scopes: scopes})
|
|> Changeset.change(%{scopes: scopes})
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1587,35 +1511,6 @@ def delete_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
|
||||||
json(conn, %{})
|
json(conn, %{})
|
||||||
end
|
end
|
||||||
|
|
||||||
# fallback action
|
|
||||||
#
|
|
||||||
def errors(conn, {:error, %Changeset{} = changeset}) do
|
|
||||||
error_message =
|
|
||||||
changeset
|
|
||||||
|> Changeset.traverse_errors(fn {message, _opt} -> message end)
|
|
||||||
|> Enum.map_join(", ", fn {_k, v} -> v end)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_status(:unprocessable_entity)
|
|
||||||
|> json(%{error: error_message})
|
|
||||||
end
|
|
||||||
|
|
||||||
def errors(conn, {:error, :not_found}) do
|
|
||||||
render_error(conn, :not_found, "Record not found")
|
|
||||||
end
|
|
||||||
|
|
||||||
def errors(conn, {:error, error_message}) do
|
|
||||||
conn
|
|
||||||
|> put_status(:bad_request)
|
|
||||||
|> json(%{error: error_message})
|
|
||||||
end
|
|
||||||
|
|
||||||
def errors(conn, _) do
|
|
||||||
conn
|
|
||||||
|> put_status(:internal_server_error)
|
|
||||||
|> json(dgettext("errors", "Something went wrong"))
|
|
||||||
end
|
|
||||||
|
|
||||||
def suggestions(%{assigns: %{user: user}} = conn, _) do
|
def suggestions(%{assigns: %{user: user}} = conn, _) do
|
||||||
suggestions = Config.get(:suggestions)
|
suggestions = Config.get(:suggestions)
|
||||||
|
|
|
@ -64,8 +64,6 @@ def errors(conn, {:error, :not_found}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def errors(conn, _) do
|
def errors(conn, _) do
|
||||||
conn
|
Pleroma.Web.MastodonAPI.FallbackController.call(conn, nil)
|
||||||
|> put_status(:internal_server_error)
|
|
||||||
|> json(dgettext("errors", "Something went wrong"))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -6,11 +6,11 @@ defmodule Pleroma.Web.MastodonAPI.ListView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
alias Pleroma.Web.MastodonAPI.ListView
|
alias Pleroma.Web.MastodonAPI.ListView
|
||||||
|
|
||||||
def render("lists.json", %{lists: lists} = opts) do
|
def render("index.json", %{lists: lists} = opts) do
|
||||||
render_many(lists, ListView, "list.json", opts)
|
render_many(lists, ListView, "show.json", opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("list.json", %{list: list}) do
|
def render("show.json", %{list: list}) do
|
||||||
%{
|
%{
|
||||||
id: to_string(list.id),
|
id: to_string(list.id),
|
||||||
title: list.title
|
title: list.title
|
||||||
|
|
|
@ -299,7 +299,8 @@ def render("status.json", %{activity: %{data: %{"object" => _object}} = activity
|
||||||
content: %{"text/plain" => content_plaintext},
|
content: %{"text/plain" => content_plaintext},
|
||||||
spoiler_text: %{"text/plain" => summary_plaintext},
|
spoiler_text: %{"text/plain" => summary_plaintext},
|
||||||
expires_at: expires_at,
|
expires_at: expires_at,
|
||||||
direct_conversation_id: direct_conversation_id
|
direct_conversation_id: direct_conversation_id,
|
||||||
|
thread_muted: thread_muted?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,8 +8,10 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
||||||
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 7]
|
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 7]
|
||||||
|
|
||||||
alias Pleroma.Conversation.Participation
|
alias Pleroma.Conversation.Participation
|
||||||
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.MastodonAPI.ConversationView
|
alias Pleroma.Web.MastodonAPI.ConversationView
|
||||||
|
alias Pleroma.Web.MastodonAPI.NotificationView
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
|
||||||
def conversation(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do
|
def conversation(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do
|
||||||
|
@ -70,4 +72,27 @@ def update_conversation(
|
||||||
|> render("participation.json", %{participation: participation, for: user})
|
|> render("participation.json", %{participation: participation, for: user})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def read_notification(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
|
||||||
|
with {:ok, notification} <- Notification.read_one(user, notification_id) do
|
||||||
|
conn
|
||||||
|
|> put_view(NotificationView)
|
||||||
|
|> render("show.json", %{notification: notification, for: user})
|
||||||
|
else
|
||||||
|
{:error, message} ->
|
||||||
|
conn
|
||||||
|
|> put_status(:bad_request)
|
||||||
|
|> json(%{"error" => message})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_notification(%{assigns: %{user: user}} = conn, %{"max_id" => max_id}) do
|
||||||
|
with notifications <- Notification.set_read_up_to(user, max_id) do
|
||||||
|
notifications = Enum.take(notifications, 80)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_view(NotificationView)
|
||||||
|
|> render("index.json", %{notifications: notifications, for: user})
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -236,12 +236,6 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/blocks_import", UtilController, :blocks_import)
|
post("/blocks_import", UtilController, :blocks_import)
|
||||||
post("/follow_import", UtilController, :follow_import)
|
post("/follow_import", UtilController, :follow_import)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope [] do
|
|
||||||
pipe_through(:oauth_read)
|
|
||||||
|
|
||||||
post("/notifications/read", UtilController, :notifications_read)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/oauth", Pleroma.Web.OAuth do
|
scope "/oauth", Pleroma.Web.OAuth do
|
||||||
|
@ -277,6 +271,7 @@ defmodule Pleroma.Web.Router do
|
||||||
scope [] do
|
scope [] do
|
||||||
pipe_through(:oauth_write)
|
pipe_through(:oauth_write)
|
||||||
patch("/conversations/:id", PleromaAPIController, :update_conversation)
|
patch("/conversations/:id", PleromaAPIController, :update_conversation)
|
||||||
|
post("/notifications/read", PleromaAPIController, :read_notification)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -312,9 +307,9 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/scheduled_statuses", MastodonAPIController, :scheduled_statuses)
|
get("/scheduled_statuses", MastodonAPIController, :scheduled_statuses)
|
||||||
get("/scheduled_statuses/:id", MastodonAPIController, :show_scheduled_status)
|
get("/scheduled_statuses/:id", MastodonAPIController, :show_scheduled_status)
|
||||||
|
|
||||||
get("/lists", MastodonAPIController, :get_lists)
|
get("/lists", ListController, :index)
|
||||||
get("/lists/:id", MastodonAPIController, :get_list)
|
get("/lists/:id", ListController, :show)
|
||||||
get("/lists/:id/accounts", MastodonAPIController, :list_accounts)
|
get("/lists/:id/accounts", ListController, :list_accounts)
|
||||||
|
|
||||||
get("/domain_blocks", MastodonAPIController, :domain_blocks)
|
get("/domain_blocks", MastodonAPIController, :domain_blocks)
|
||||||
|
|
||||||
|
@ -355,12 +350,12 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/media", MastodonAPIController, :upload)
|
post("/media", MastodonAPIController, :upload)
|
||||||
put("/media/:id", MastodonAPIController, :update_media)
|
put("/media/:id", MastodonAPIController, :update_media)
|
||||||
|
|
||||||
delete("/lists/:id", MastodonAPIController, :delete_list)
|
delete("/lists/:id", ListController, :delete)
|
||||||
post("/lists", MastodonAPIController, :create_list)
|
post("/lists", ListController, :create)
|
||||||
put("/lists/:id", MastodonAPIController, :rename_list)
|
put("/lists/:id", ListController, :update)
|
||||||
|
|
||||||
post("/lists/:id/accounts", MastodonAPIController, :add_to_list)
|
post("/lists/:id/accounts", ListController, :add_to_list)
|
||||||
delete("/lists/:id/accounts", MastodonAPIController, :remove_from_list)
|
delete("/lists/:id/accounts", ListController, :remove_from_list)
|
||||||
|
|
||||||
post("/filters", MastodonAPIController, :create_filter)
|
post("/filters", MastodonAPIController, :create_filter)
|
||||||
get("/filters/:id", MastodonAPIController, :get_filter)
|
get("/filters/:id", MastodonAPIController, :get_filter)
|
||||||
|
|
56
test/fixtures/osada-follow-activity.json
vendored
Normal file
56
test/fixtures/osada-follow-activity.json
vendored
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
{
|
||||||
|
"@context":[
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
"https://apfed.club/apschema/v1.4"
|
||||||
|
],
|
||||||
|
"id":"https://apfed.club/follow/9",
|
||||||
|
"type":"Follow",
|
||||||
|
"actor":{
|
||||||
|
"type":"Person",
|
||||||
|
"id":"https://apfed.club/channel/indio",
|
||||||
|
"preferredUsername":"indio",
|
||||||
|
"name":"Indio",
|
||||||
|
"updated":"2019-08-20T23:52:34Z",
|
||||||
|
"icon":{
|
||||||
|
"type":"Image",
|
||||||
|
"mediaType":"image/jpeg",
|
||||||
|
"updated":"2019-08-20T23:53:37Z",
|
||||||
|
"url":"https://apfed.club/photo/profile/l/2",
|
||||||
|
"height":300,
|
||||||
|
"width":300
|
||||||
|
},
|
||||||
|
"url":"https://apfed.club/channel/indio",
|
||||||
|
"inbox":"https://apfed.club/inbox/indio",
|
||||||
|
"outbox":"https://apfed.club/outbox/indio",
|
||||||
|
"followers":"https://apfed.club/followers/indio",
|
||||||
|
"following":"https://apfed.club/following/indio",
|
||||||
|
"endpoints":{
|
||||||
|
"sharedInbox":"https://apfed.club/inbox"
|
||||||
|
},
|
||||||
|
"publicKey":{
|
||||||
|
"id":"https://apfed.club/channel/indio",
|
||||||
|
"owner":"https://apfed.club/channel/indio",
|
||||||
|
"publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA77TIR1VuSYFnmDRFGHHb\n4vaGdx9ranzRX4bfOKAqa++Ch5L4EqJpPy08RuM+NrYCYiYl4QQFDSSDXAEgb5g9\nC1TgWTfI7q/E0UBX2Vr0mU6X4i1ztv0tuQvegRjcSJ7l1AvoBs8Ip4MEJ3OPEQhB\ngJqAACB3Gnps4zi2I0yavkxUfGVKr6zKT3BxWh5hTpKC7Do+ChIrVZC2EwxND9K6
|
||||||
|
\nsAnQHThcb5EQuvuzUQZKeS7IEOsd0JpZDmJjbfMGrAWE81pLIfEeeA2joCJiBBTO\nglDsW+juvZ+lWqJpMr2hMWpvfrFjJeUawNJCIzsLdVIZR+aKj5yy6yqoS8hkN9Ha\n1MljZpsXl+EmwcwAIqim1YeLwERCEAQ/JWbSt8pQTQbzZ6ibwQ4mchCxacrRbIVR
|
||||||
|
\nnL59fWMBassJcbY0VwrTugm2SBsYbDjESd55UZV03Rwr8qseGTyi+hH8O7w2SIaY\nzjN6AdZiPmsh00YflzlCk8MSLOHMol1vqIUzXxU8CdXn9+KsuQdZGrTz0YKN/db4\naVwUGJatz2Tsvf7R1tJBjJfeQWOWbbn3pycLVH86LjZ83qngp9ZVnAveUnUqz0yS
|
||||||
|
\nhe+buZ6UMsfGzbIYon2bKNlz6gYTH0YPcr+cLe+29drtt0GZiXha1agbpo4RB8zE
|
||||||
|
\naNL2fucF5YT0yNpbd/5WoV0CAwEAAQ==\n-----END PUBLIC KEY-----\n"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"object":"https://pleroma.site/users/kaniini",
|
||||||
|
"to":[
|
||||||
|
"https://pleroma.site/users/kaniini"
|
||||||
|
],
|
||||||
|
"signature":{
|
||||||
|
"@context":[
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1"
|
||||||
|
],
|
||||||
|
"type":"RsaSignature2017",
|
||||||
|
"nonce":"52c035e0a9e81dce8b486159204e97c22637e91f75cdfad5378de91de68e9117",
|
||||||
|
"creator":"https://apfed.club/channel/indio/public_key_pem",
|
||||||
|
"created":"2019-08-22T03:38:02Z",
|
||||||
|
"signatureValue":"oVliRCIqNIh6yUp851dYrF0y21aHp3Rz6VkIpW1pFMWfXuzExyWSfcELpyLseeRmsw5bUu9zJkH44B4G2LiJQKA9UoEQDjrDMZBmbeUpiQqq3DVUzkrBOI8bHZ7xyJ/CjSZcNHHh0MHhSKxswyxWMGi4zIqzkAZG3vRRgoPVHdjPm00sR3B8jBLw1cjoffv+KKeM/zEUpe13gqX9qHAWHHqZepxgSWmq+EKOkRvHUPBXiEJZfXzc5uW+vZ09F3WBYmaRoy8Y0e1P29fnRLqSy7EEINdrHaGclRqoUZyiawpkgy3lWWlynesV/HiLBR7EXT79eKstxf4wfTDaPKBCfTCsOWuMWHr7Genu37ew2/t7eiBGqCwwW12ylhml/OLHgNK3LOhmRABhtfpaFZSxfDVnlXfaLpY1xekVOj2oC0FpBtnoxVKLpIcyLw6dkfSil5ANd+hl59W/bpPA8KT90ii1fSNCo3+FcwQVx0YsPznJNA60XfFuVsme7zNcOst6393e1WriZxBanFpfB63zVQc9u1fjyfktx/yiUNxIlre+sz9OCc0AACn94iRhBYh4bbzdleUOTnM7lnD4Dj2FP+xeDIP8CA8wXUeq5+9kopSp2kAmlUEyFUdg4no7naIeu1SZnopfUg56PsVCp9JHiUK1SYAyWbdC+FbUECu5CvI="
|
||||||
|
}
|
||||||
|
}
|
1
test/fixtures/tesla_mock/osada-user-indio.json
vendored
Normal file
1
test/fixtures/tesla_mock/osada-user-indio.json
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],"type":"Person","id":"https://apfed.club/channel/indio","preferredUsername":"indio","name":"Indio","updated":"2019-08-20T23:52:34Z","icon":{"type":"Image","mediaType":"image/jpeg","updated":"2019-08-20T23:53:37Z","url":"https://apfed.club/photo/profile/l/2","height":300,"width":300},"url":"https://apfed.club/channel/indio","inbox":"https://apfed.club/inbox/indio","outbox":"https://apfed.club/outbox/indio","followers":"https://apfed.club/followers/indio","following":"https://apfed.club/following/indio","endpoints":{"sharedInbox":"https://apfed.club/inbox"},"publicKey":{"id":"https://apfed.club/channel/indio","owner":"https://apfed.club/channel/indio","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA77TIR1VuSYFnmDRFGHHb\n4vaGdx9ranzRX4bfOKAqa++Ch5L4EqJpPy08RuM+NrYCYiYl4QQFDSSDXAEgb5g9\nC1TgWTfI7q/E0UBX2Vr0mU6X4i1ztv0tuQvegRjcSJ7l1AvoBs8Ip4MEJ3OPEQhB\ngJqAACB3Gnps4zi2I0yavkxUfGVKr6zKT3BxWh5hTpKC7Do+ChIrVZC2EwxND9K6\nsAnQHThcb5EQuvuzUQZKeS7IEOsd0JpZDmJjbfMGrAWE81pLIfEeeA2joCJiBBTO\nglDsW+juvZ+lWqJpMr2hMWpvfrFjJeUawNJCIzsLdVIZR+aKj5yy6yqoS8hkN9Ha\n1MljZpsXl+EmwcwAIqim1YeLwERCEAQ/JWbSt8pQTQbzZ6ibwQ4mchCxacrRbIVR\nnL59fWMBassJcbY0VwrTugm2SBsYbDjESd55UZV03Rwr8qseGTyi+hH8O7w2SIaY\nzjN6AdZiPmsh00YflzlCk8MSLOHMol1vqIUzXxU8CdXn9+KsuQdZGrTz0YKN/db4\naVwUGJatz2Tsvf7R1tJBjJfeQWOWbbn3pycLVH86LjZ83qngp9ZVnAveUnUqz0yS\nhe+buZ6UMsfGzbIYon2bKNlz6gYTH0YPcr+cLe+29drtt0GZiXha1agbpo4RB8zE\naNL2fucF5YT0yNpbd/5WoV0CAwEAAQ==\n-----END PUBLIC KEY-----\n"},"signature":{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],"type":"RsaSignature2017","nonce":"c672a408d2e88b322b36a61bf0c25f586be9245d30293c55b8d653dcc867aaf7","creator":"https://apfed.club/channel/indio/public_key_pem","created":"2019-08-26T07:24:03Z","signatureValue":"MyAv5gnedu6L/DYFaE1TUYvp4LjI9ZUU0axwGYOhgD7qsjivMgwbOrjX/iH32xlcfF8nWOMh/ogu3+Qwr5sqLHkS2AimWmw1+Ubf2KccE58b8vI8zWfyu8QJnMuE92jtBPv8UTQUHw8ZebbExk3L99oXaeyVihKiMBmd63NpVTpGXZTg6m+H+KfWchVajPoyNKZtKMd3nH99x5j54Cqkz0BN5CSTwCSG0wP95G0VtZHtmhX+tsAPM3oAj0d+gtCZSCd8Nu8fvFAwCyTg1oKSfRqKb27EKHlskqK9X57x0jURH77CTAIQSejgGcKJ5GGLtvofubJkafadjagqrtqz6Mz6BZ642ssJ2KGkRAn79Q4F08goI6cfU5lLk2Tooe5A55XERnmE3SkYGyTvLpacZplxJdU0sa+deX9D7+alSGFJZSziaxpCxzrO6lEApe4b9kHXAzn9VaZt9trijkHq/kkq0i3NRcP7n8JG9q+Vv8jY9ddY6HcH89RNCBIA6MKLtAqc+vSc5G24qeZlw2MzlQWBp0KGuVG8DQR00AL6cXLBzF1WY8JZeEg6zqm+DMznbuNzgiS34BP+AehBSHlQ4MZebwDnK3ZPPqGSwioIWMxIFfZDaVDX9Pp1pXAARQMw0c/y4sDcf9FMzsr8jteEa7ZQcoqq5kXQTSCP56TEHnI="}}
|
|
@ -15,6 +15,13 @@ test "creating a list" do
|
||||||
assert title == "title"
|
assert title == "title"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "validates title" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
assert {:error, changeset} = Pleroma.List.create("", user)
|
||||||
|
assert changeset.errors == [title: {"can't be blank", [validation: :required]}]
|
||||||
|
end
|
||||||
|
|
||||||
test "getting a list not belonging to the user" do
|
test "getting a list not belonging to the user" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
|
78
test/pagination_test.exs
Normal file
78
test/pagination_test.exs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.PaginationTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Pagination
|
||||||
|
|
||||||
|
describe "keyset" do
|
||||||
|
setup do
|
||||||
|
notes = insert_list(5, :note)
|
||||||
|
|
||||||
|
%{notes: notes}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "paginates by min_id", %{notes: notes} do
|
||||||
|
id = Enum.at(notes, 2).id |> Integer.to_string()
|
||||||
|
|
||||||
|
%{total: total, items: paginated} =
|
||||||
|
Pagination.fetch_paginated(Object, %{"min_id" => id, "total" => true})
|
||||||
|
|
||||||
|
assert length(paginated) == 2
|
||||||
|
assert total == 5
|
||||||
|
end
|
||||||
|
|
||||||
|
test "paginates by since_id", %{notes: notes} do
|
||||||
|
id = Enum.at(notes, 2).id |> Integer.to_string()
|
||||||
|
|
||||||
|
%{total: total, items: paginated} =
|
||||||
|
Pagination.fetch_paginated(Object, %{"since_id" => id, "total" => true})
|
||||||
|
|
||||||
|
assert length(paginated) == 2
|
||||||
|
assert total == 5
|
||||||
|
end
|
||||||
|
|
||||||
|
test "paginates by max_id", %{notes: notes} do
|
||||||
|
id = Enum.at(notes, 1).id |> Integer.to_string()
|
||||||
|
|
||||||
|
%{total: total, items: paginated} =
|
||||||
|
Pagination.fetch_paginated(Object, %{"max_id" => id, "total" => true})
|
||||||
|
|
||||||
|
assert length(paginated) == 1
|
||||||
|
assert total == 5
|
||||||
|
end
|
||||||
|
|
||||||
|
test "paginates by min_id & limit", %{notes: notes} do
|
||||||
|
id = Enum.at(notes, 2).id |> Integer.to_string()
|
||||||
|
|
||||||
|
paginated = Pagination.fetch_paginated(Object, %{"min_id" => id, "limit" => 1})
|
||||||
|
|
||||||
|
assert length(paginated) == 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "offset" do
|
||||||
|
setup do
|
||||||
|
notes = insert_list(5, :note)
|
||||||
|
|
||||||
|
%{notes: notes}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "paginates by limit" do
|
||||||
|
paginated = Pagination.fetch_paginated(Object, %{"limit" => 2}, :offset)
|
||||||
|
|
||||||
|
assert length(paginated) == 2
|
||||||
|
end
|
||||||
|
|
||||||
|
test "paginates by limit & offset" do
|
||||||
|
paginated = Pagination.fetch_paginated(Object, %{"limit" => 2, "offset" => 4}, :offset)
|
||||||
|
|
||||||
|
assert length(paginated) == 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -207,13 +207,15 @@ def like_activity_factory(attrs \\ %{}) do
|
||||||
object = Object.normalize(note_activity)
|
object = Object.normalize(note_activity)
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
data = %{
|
data =
|
||||||
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
|
%{
|
||||||
"actor" => user.ap_id,
|
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
|
||||||
"type" => "Like",
|
"actor" => user.ap_id,
|
||||||
"object" => object.data["id"],
|
"type" => "Like",
|
||||||
"published_at" => DateTime.utc_now() |> DateTime.to_iso8601()
|
"object" => object.data["id"],
|
||||||
}
|
"published_at" => DateTime.utc_now() |> DateTime.to_iso8601()
|
||||||
|
}
|
||||||
|
|> Map.merge(attrs[:data_attrs] || %{})
|
||||||
|
|
||||||
%Pleroma.Activity{
|
%Pleroma.Activity{
|
||||||
data: data
|
data: data
|
||||||
|
|
|
@ -775,6 +775,11 @@ def get("https://mastodon.social/users/lambadalambda", _, _, _) do
|
||||||
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/lambadalambda.json")}}
|
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/lambadalambda.json")}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get("https://apfed.club/channel/indio", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/osada-user-indio.json")}}
|
||||||
|
end
|
||||||
|
|
||||||
def get("https://social.heldscal.la/user/23211", _, _, Accept: "application/activity+json") do
|
def get("https://social.heldscal.la/user/23211", _, _, Accept: "application/activity+json") do
|
||||||
{:ok, Tesla.Mock.json(%{"id" => "https://social.heldscal.la/user/23211"}, status: 200)}
|
{:ok, Tesla.Mock.json(%{"id" => "https://social.heldscal.la/user/23211"}, status: 200)}
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,6 +21,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
clear_config([:instance, :federating])
|
||||||
|
|
||||||
describe "streaming out participations" do
|
describe "streaming out participations" do
|
||||||
test "it streams them out" do
|
test "it streams them out" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
@ -676,6 +678,29 @@ test "returns reblogs for users for whom reblogs have not been muted" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "like an object" do
|
describe "like an object" do
|
||||||
|
test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
|
||||||
|
Pleroma.Config.put([:instance, :federating], true)
|
||||||
|
note_activity = insert(:note_activity)
|
||||||
|
assert object_activity = Object.normalize(note_activity)
|
||||||
|
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
|
||||||
|
assert called(Pleroma.Web.Federator.publish(like_activity, 5))
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns exist activity if object already liked" do
|
||||||
|
note_activity = insert(:note_activity)
|
||||||
|
assert object_activity = Object.normalize(note_activity)
|
||||||
|
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
|
||||||
|
|
||||||
|
{:ok, like_activity_exist, _object} = ActivityPub.like(user, object_activity)
|
||||||
|
assert like_activity == like_activity_exist
|
||||||
|
end
|
||||||
|
|
||||||
test "adds a like activity to the db" do
|
test "adds a like activity to the db" do
|
||||||
note_activity = insert(:note_activity)
|
note_activity = insert(:note_activity)
|
||||||
assert object = Object.normalize(note_activity)
|
assert object = Object.normalize(note_activity)
|
||||||
|
@ -706,6 +731,25 @@ test "adds a like activity to the db" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "unliking" do
|
describe "unliking" do
|
||||||
|
test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
|
||||||
|
Pleroma.Config.put([:instance, :federating], true)
|
||||||
|
|
||||||
|
note_activity = insert(:note_activity)
|
||||||
|
object = Object.normalize(note_activity)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, object} = ActivityPub.unlike(user, object)
|
||||||
|
refute called(Pleroma.Web.Federator.publish())
|
||||||
|
|
||||||
|
{:ok, _like_activity, object} = ActivityPub.like(user, object)
|
||||||
|
assert object.data["like_count"] == 1
|
||||||
|
|
||||||
|
{:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
|
||||||
|
assert object.data["like_count"] == 0
|
||||||
|
|
||||||
|
assert called(Pleroma.Web.Federator.publish(unlike_activity, 5))
|
||||||
|
end
|
||||||
|
|
||||||
test "unliking a previously liked object" do
|
test "unliking a previously liked object" do
|
||||||
note_activity = insert(:note_activity)
|
note_activity = insert(:note_activity)
|
||||||
object = Object.normalize(note_activity)
|
object = Object.normalize(note_activity)
|
||||||
|
|
|
@ -19,6 +19,25 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "handle_incoming" do
|
describe "handle_incoming" do
|
||||||
|
test "it works for osada follow request" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/osada-follow-activity.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("object", user.ap_id)
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert data["actor"] == "https://apfed.club/channel/indio"
|
||||||
|
assert data["type"] == "Follow"
|
||||||
|
assert data["id"] == "https://apfed.club/follow/9"
|
||||||
|
|
||||||
|
activity = Repo.get(Activity, activity.id)
|
||||||
|
assert activity.data["state"] == "accept"
|
||||||
|
assert User.following?(User.get_cached_by_ap_id(data["actor"]), user)
|
||||||
|
end
|
||||||
|
|
||||||
test "it works for incoming follow requests" do
|
test "it works for incoming follow requests" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
describe "fetch the latest Follow" do
|
describe "fetch the latest Follow" do
|
||||||
test "fetches the latest Follow activity" do
|
test "fetches the latest Follow activity" do
|
||||||
%Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
|
%Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
|
||||||
|
@ -87,6 +89,32 @@ test "works with an object that has only IR tags" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "make_unlike_data/3" do
|
||||||
|
test "returns data for unlike activity" do
|
||||||
|
user = insert(:user)
|
||||||
|
like_activity = insert(:like_activity, data_attrs: %{"context" => "test context"})
|
||||||
|
|
||||||
|
assert Utils.make_unlike_data(user, like_activity, nil) == %{
|
||||||
|
"type" => "Undo",
|
||||||
|
"actor" => user.ap_id,
|
||||||
|
"object" => like_activity.data,
|
||||||
|
"to" => [user.follower_address, like_activity.data["actor"]],
|
||||||
|
"cc" => [Pleroma.Constants.as_public()],
|
||||||
|
"context" => like_activity.data["context"]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Utils.make_unlike_data(user, like_activity, "9mJEZK0tky1w2xD2vY") == %{
|
||||||
|
"type" => "Undo",
|
||||||
|
"actor" => user.ap_id,
|
||||||
|
"object" => like_activity.data,
|
||||||
|
"to" => [user.follower_address, like_activity.data["actor"]],
|
||||||
|
"cc" => [Pleroma.Constants.as_public()],
|
||||||
|
"context" => like_activity.data["context"],
|
||||||
|
"id" => "9mJEZK0tky1w2xD2vY"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "make_like_data" do
|
describe "make_like_data" do
|
||||||
setup do
|
setup do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
@ -299,4 +327,78 @@ test "updates the state of the given follow activity" do
|
||||||
assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
|
assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "update_element_in_object/3" do
|
||||||
|
test "updates likes" do
|
||||||
|
user = insert(:user)
|
||||||
|
activity = insert(:note_activity)
|
||||||
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
|
assert {:ok, updated_object} =
|
||||||
|
Utils.update_element_in_object(
|
||||||
|
"like",
|
||||||
|
[user.ap_id],
|
||||||
|
object
|
||||||
|
)
|
||||||
|
|
||||||
|
assert updated_object.data["likes"] == [user.ap_id]
|
||||||
|
assert updated_object.data["like_count"] == 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "add_like_to_object/2" do
|
||||||
|
test "add actor to likes" do
|
||||||
|
user = insert(:user)
|
||||||
|
user2 = insert(:user)
|
||||||
|
object = insert(:note)
|
||||||
|
|
||||||
|
assert {:ok, updated_object} =
|
||||||
|
Utils.add_like_to_object(
|
||||||
|
%Activity{data: %{"actor" => user.ap_id}},
|
||||||
|
object
|
||||||
|
)
|
||||||
|
|
||||||
|
assert updated_object.data["likes"] == [user.ap_id]
|
||||||
|
assert updated_object.data["like_count"] == 1
|
||||||
|
|
||||||
|
assert {:ok, updated_object2} =
|
||||||
|
Utils.add_like_to_object(
|
||||||
|
%Activity{data: %{"actor" => user2.ap_id}},
|
||||||
|
updated_object
|
||||||
|
)
|
||||||
|
|
||||||
|
assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id]
|
||||||
|
assert updated_object2.data["like_count"] == 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "remove_like_from_object/2" do
|
||||||
|
test "removes ap_id from likes" do
|
||||||
|
user = insert(:user)
|
||||||
|
user2 = insert(:user)
|
||||||
|
object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2})
|
||||||
|
|
||||||
|
assert {:ok, updated_object} =
|
||||||
|
Utils.remove_like_from_object(
|
||||||
|
%Activity{data: %{"actor" => user.ap_id}},
|
||||||
|
object
|
||||||
|
)
|
||||||
|
|
||||||
|
assert updated_object.data["likes"] == [user2.ap_id]
|
||||||
|
assert updated_object.data["like_count"] == 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "get_existing_like/2" do
|
||||||
|
test "fetches existing like" do
|
||||||
|
note_activity = insert(:note_activity)
|
||||||
|
assert object = Object.normalize(note_activity)
|
||||||
|
|
||||||
|
user = insert(:user)
|
||||||
|
refute Utils.get_existing_like(user.ap_id, object)
|
||||||
|
{:ok, like_activity, _object} = ActivityPub.like(user, object)
|
||||||
|
|
||||||
|
assert ^like_activity = Utils.get_existing_like(user.ap_id, object)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
166
test/web/mastodon_api/controllers/list_controller_test.exs
Normal file
166
test/web/mastodon_api/controllers/list_controller_test.exs
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.ListControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
test "creating a list", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/lists", %{"title" => "cuties"})
|
||||||
|
|
||||||
|
assert %{"title" => title} = json_response(conn, 200)
|
||||||
|
assert title == "cuties"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "renders error for invalid params", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/lists", %{"title" => nil})
|
||||||
|
|
||||||
|
assert %{"error" => "can't be blank"} == json_response(conn, :unprocessable_entity)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "listing a user's lists", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/lists", %{"title" => "cuties"})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/lists", %{"title" => "cofe"})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/lists")
|
||||||
|
|
||||||
|
assert [
|
||||||
|
%{"id" => _, "title" => "cofe"},
|
||||||
|
%{"id" => _, "title" => "cuties"}
|
||||||
|
] = json_response(conn, :ok)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "adding users to a list", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
{:ok, list} = Pleroma.List.create("name", user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
|
||||||
|
|
||||||
|
assert %{} == json_response(conn, 200)
|
||||||
|
%Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
|
||||||
|
assert following == [other_user.follower_address]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "removing users from a list", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
third_user = insert(:user)
|
||||||
|
{:ok, list} = Pleroma.List.create("name", user)
|
||||||
|
{:ok, list} = Pleroma.List.follow(list, other_user)
|
||||||
|
{:ok, list} = Pleroma.List.follow(list, third_user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
|
||||||
|
|
||||||
|
assert %{} == json_response(conn, 200)
|
||||||
|
%Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
|
||||||
|
assert following == [third_user.follower_address]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "listing users in a list", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
{:ok, list} = Pleroma.List.create("name", user)
|
||||||
|
{:ok, list} = Pleroma.List.follow(list, other_user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
|
||||||
|
|
||||||
|
assert [%{"id" => id}] = json_response(conn, 200)
|
||||||
|
assert id == to_string(other_user.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "retrieving a list", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, list} = Pleroma.List.create("name", user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/lists/#{list.id}")
|
||||||
|
|
||||||
|
assert %{"id" => id} = json_response(conn, 200)
|
||||||
|
assert id == to_string(list.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "renders 404 if list is not found", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/lists/666")
|
||||||
|
|
||||||
|
assert %{"error" => "List not found"} = json_response(conn, :not_found)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "renaming a list", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, list} = Pleroma.List.create("name", user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
|
||||||
|
|
||||||
|
assert %{"title" => name} = json_response(conn, 200)
|
||||||
|
assert name == "newname"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "validates title when renaming a list", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, list} = Pleroma.List.create("name", user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> put("/api/v1/lists/#{list.id}", %{"title" => " "})
|
||||||
|
|
||||||
|
assert %{"error" => "can't be blank"} == json_response(conn, :unprocessable_entity)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "deleting a list", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, list} = Pleroma.List.create("name", user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> delete("/api/v1/lists/#{list.id}")
|
||||||
|
|
||||||
|
assert %{} = json_response(conn, 200)
|
||||||
|
assert is_nil(Repo.get(Pleroma.List, list.id))
|
||||||
|
end
|
||||||
|
end
|
|
@ -927,106 +927,7 @@ test "delete a filter", %{conn: conn} do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "lists" do
|
describe "list timelines" do
|
||||||
test "creating a list", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> post("/api/v1/lists", %{"title" => "cuties"})
|
|
||||||
|
|
||||||
assert %{"title" => title} = json_response(conn, 200)
|
|
||||||
assert title == "cuties"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "adding users to a list", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
other_user = insert(:user)
|
|
||||||
{:ok, list} = Pleroma.List.create("name", user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
|
|
||||||
|
|
||||||
assert %{} == json_response(conn, 200)
|
|
||||||
%Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
|
|
||||||
assert following == [other_user.follower_address]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "removing users from a list", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
other_user = insert(:user)
|
|
||||||
third_user = insert(:user)
|
|
||||||
{:ok, list} = Pleroma.List.create("name", user)
|
|
||||||
{:ok, list} = Pleroma.List.follow(list, other_user)
|
|
||||||
{:ok, list} = Pleroma.List.follow(list, third_user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
|
|
||||||
|
|
||||||
assert %{} == json_response(conn, 200)
|
|
||||||
%Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
|
|
||||||
assert following == [third_user.follower_address]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "listing users in a list", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
other_user = insert(:user)
|
|
||||||
{:ok, list} = Pleroma.List.create("name", user)
|
|
||||||
{:ok, list} = Pleroma.List.follow(list, other_user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
|
|
||||||
|
|
||||||
assert [%{"id" => id}] = json_response(conn, 200)
|
|
||||||
assert id == to_string(other_user.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "retrieving a list", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
{:ok, list} = Pleroma.List.create("name", user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> get("/api/v1/lists/#{list.id}")
|
|
||||||
|
|
||||||
assert %{"id" => id} = json_response(conn, 200)
|
|
||||||
assert id == to_string(list.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "renaming a list", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
{:ok, list} = Pleroma.List.create("name", user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
|
|
||||||
|
|
||||||
assert %{"title" => name} = json_response(conn, 200)
|
|
||||||
assert name == "newname"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "deleting a list", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
{:ok, list} = Pleroma.List.create("name", user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> delete("/api/v1/lists/#{list.id}")
|
|
||||||
|
|
||||||
assert %{} = json_response(conn, 200)
|
|
||||||
assert is_nil(Repo.get(Pleroma.List, list.id))
|
|
||||||
end
|
|
||||||
|
|
||||||
test "list timeline", %{conn: conn} do
|
test "list timeline", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
|
|
@ -7,7 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.ListViewTest do
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
alias Pleroma.Web.MastodonAPI.ListView
|
alias Pleroma.Web.MastodonAPI.ListView
|
||||||
|
|
||||||
test "Represent a list" do
|
test "show" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
title = "mortal enemies"
|
title = "mortal enemies"
|
||||||
{:ok, list} = Pleroma.List.create(title, user)
|
{:ok, list} = Pleroma.List.create(title, user)
|
||||||
|
@ -17,6 +17,16 @@ test "Represent a list" do
|
||||||
title: title
|
title: title
|
||||||
}
|
}
|
||||||
|
|
||||||
assert expected == ListView.render("list.json", %{list: list})
|
assert expected == ListView.render("show.json", %{list: list})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "index" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, list} = Pleroma.List.create("my list", user)
|
||||||
|
{:ok, list2} = Pleroma.List.create("cofe", user)
|
||||||
|
|
||||||
|
assert [%{id: _, title: "my list"}, %{id: _, title: "cofe"}] =
|
||||||
|
ListView.render("index.json", lists: [list, list2])
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -150,7 +150,8 @@ test "a note activity" do
|
||||||
content: %{"text/plain" => HtmlSanitizeEx.strip_tags(object_data["content"])},
|
content: %{"text/plain" => HtmlSanitizeEx.strip_tags(object_data["content"])},
|
||||||
spoiler_text: %{"text/plain" => HtmlSanitizeEx.strip_tags(object_data["summary"])},
|
spoiler_text: %{"text/plain" => HtmlSanitizeEx.strip_tags(object_data["summary"])},
|
||||||
expires_at: nil,
|
expires_at: nil,
|
||||||
direct_conversation_id: nil
|
direct_conversation_id: nil,
|
||||||
|
thread_muted: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,6 +174,24 @@ test "tells if the message is muted for some reason" do
|
||||||
assert status.muted == true
|
assert status.muted == true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "tells if the message is thread muted" do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, user} = User.mute(user, other_user)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "test"})
|
||||||
|
status = StatusView.render("status.json", %{activity: activity, for: user})
|
||||||
|
|
||||||
|
assert status.pleroma.thread_muted == false
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.add_mute(user, activity)
|
||||||
|
|
||||||
|
status = StatusView.render("status.json", %{activity: activity, for: user})
|
||||||
|
|
||||||
|
assert status.pleroma.thread_muted == true
|
||||||
|
end
|
||||||
|
|
||||||
test "tells if the status is bookmarked" do
|
test "tells if the status is bookmarked" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
alias Pleroma.Conversation.Participation
|
alias Pleroma.Conversation.Participation
|
||||||
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
@ -91,4 +92,59 @@ test "PATCH /api/v1/pleroma/conversations/:id", %{conn: conn} do
|
||||||
assert user in participation.recipients
|
assert user in participation.recipients
|
||||||
assert other_user in participation.recipients
|
assert other_user in participation.recipients
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "POST /api/v1/pleroma/notifications/read" do
|
||||||
|
test "it marks a single notification as read", %{conn: conn} do
|
||||||
|
user1 = insert(:user)
|
||||||
|
user2 = insert(:user)
|
||||||
|
{:ok, activity1} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"})
|
||||||
|
{:ok, activity2} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"})
|
||||||
|
{:ok, [notification1]} = Notification.create_notifications(activity1)
|
||||||
|
{:ok, [notification2]} = Notification.create_notifications(activity2)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user1)
|
||||||
|
|> post("/api/v1/pleroma/notifications/read", %{"id" => "#{notification1.id}"})
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
assert %{"pleroma" => %{"is_seen" => true}} = response
|
||||||
|
assert Repo.get(Notification, notification1.id).seen
|
||||||
|
refute Repo.get(Notification, notification2.id).seen
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it marks multiple notifications as read", %{conn: conn} do
|
||||||
|
user1 = insert(:user)
|
||||||
|
user2 = insert(:user)
|
||||||
|
{:ok, _activity1} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"})
|
||||||
|
{:ok, _activity2} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"})
|
||||||
|
{:ok, _activity3} = CommonAPI.post(user2, %{"status" => "HIE @#{user1.nickname}"})
|
||||||
|
|
||||||
|
[notification3, notification2, notification1] = Notification.for_user(user1, %{limit: 3})
|
||||||
|
|
||||||
|
[response1, response2] =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user1)
|
||||||
|
|> post("/api/v1/pleroma/notifications/read", %{"max_id" => "#{notification2.id}"})
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
assert %{"pleroma" => %{"is_seen" => true}} = response1
|
||||||
|
assert %{"pleroma" => %{"is_seen" => true}} = response2
|
||||||
|
assert Repo.get(Notification, notification1.id).seen
|
||||||
|
assert Repo.get(Notification, notification2.id).seen
|
||||||
|
refute Repo.get(Notification, notification3.id).seen
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns error when notification not found", %{conn: conn} do
|
||||||
|
user1 = insert(:user)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user1)
|
||||||
|
|> post("/api/v1/pleroma/notifications/read", %{"id" => "22222222222222"})
|
||||||
|
|> json_response(:bad_request)
|
||||||
|
|
||||||
|
assert response == %{"error" => "Cannot get notification"}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
|
defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
alias Pleroma.Notification
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
@ -141,37 +140,6 @@ test "it imports blocks users from file", %{conn: conn} do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /api/pleroma/notifications/read" do
|
|
||||||
test "it marks a single notification as read", %{conn: conn} do
|
|
||||||
user1 = insert(:user)
|
|
||||||
user2 = insert(:user)
|
|
||||||
{:ok, activity1} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"})
|
|
||||||
{:ok, activity2} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"})
|
|
||||||
{:ok, [notification1]} = Notification.create_notifications(activity1)
|
|
||||||
{:ok, [notification2]} = Notification.create_notifications(activity2)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> assign(:user, user1)
|
|
||||||
|> post("/api/pleroma/notifications/read", %{"id" => "#{notification1.id}"})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert Repo.get(Notification, notification1.id).seen
|
|
||||||
refute Repo.get(Notification, notification2.id).seen
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it returns error when notification not found", %{conn: conn} do
|
|
||||||
user1 = insert(:user)
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user1)
|
|
||||||
|> post("/api/pleroma/notifications/read", %{"id" => "22222222222222"})
|
|
||||||
|> json_response(403)
|
|
||||||
|
|
||||||
assert response == %{"error" => "Cannot get notification"}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "PUT /api/pleroma/notification_settings" do
|
describe "PUT /api/pleroma/notification_settings" do
|
||||||
test "it updates notification settings", %{conn: conn} do
|
test "it updates notification settings", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
Loading…
Reference in a new issue