forked from AkkomaGang/akkoma
Merge branch 'develop' into 'reactions'
# Conflicts: # CHANGELOG.md
This commit is contained in:
commit
61097ba6ab
28 changed files with 592 additions and 187 deletions
|
@ -56,6 +56,7 @@ Has these additional fields under the `pleroma` object:
|
||||||
- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`
|
- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`
|
||||||
- `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials`
|
- `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials`
|
||||||
- `deactivated`: boolean, true when the user is deactivated
|
- `deactivated`: boolean, true when the user is deactivated
|
||||||
|
- `unread_conversation_count`: The count of unread conversations. Only returned to the account owner.
|
||||||
|
|
||||||
### Source
|
### Source
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,8 @@ def create_or_bump_for(activity, opts \\ []) do
|
||||||
|
|
||||||
participations =
|
participations =
|
||||||
Enum.map(users, fn user ->
|
Enum.map(users, fn user ->
|
||||||
|
User.increment_unread_conversation_count(conversation, user)
|
||||||
|
|
||||||
{:ok, participation} =
|
{:ok, participation} =
|
||||||
Participation.create_for_user_and_conversation(user, conversation, opts)
|
Participation.create_for_user_and_conversation(user, conversation, opts)
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,15 @@ def mark_as_read(participation) do
|
||||||
participation
|
participation
|
||||||
|> read_cng(%{read: true})
|
|> read_cng(%{read: true})
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
|
|> case do
|
||||||
|
{:ok, participation} ->
|
||||||
|
participation = Repo.preload(participation, :user)
|
||||||
|
User.set_unread_conversation_count(participation.user)
|
||||||
|
{:ok, participation}
|
||||||
|
|
||||||
|
error ->
|
||||||
|
error
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def mark_as_unread(participation) do
|
def mark_as_unread(participation) do
|
||||||
|
@ -135,4 +144,12 @@ def set_recipients(participation, user_ids) do
|
||||||
|
|
||||||
{:ok, Repo.preload(participation, :recipients, force: true)}
|
{:ok, Repo.preload(participation, :recipients, force: true)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def unread_conversation_count_for_user(user) do
|
||||||
|
from(p in __MODULE__,
|
||||||
|
where: p.user_id == ^user.id,
|
||||||
|
where: not p.read,
|
||||||
|
select: %{count: count(p.id)}
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.User do
|
||||||
alias Comeonin.Pbkdf2
|
alias Comeonin.Pbkdf2
|
||||||
alias Ecto.Multi
|
alias Ecto.Multi
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Conversation.Participation
|
||||||
alias Pleroma.Delivery
|
alias Pleroma.Delivery
|
||||||
alias Pleroma.Keys
|
alias Pleroma.Keys
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
|
@ -842,6 +843,61 @@ def maybe_update_following_count(%User{local: false} = user) do
|
||||||
|
|
||||||
def maybe_update_following_count(user), do: user
|
def maybe_update_following_count(user), do: user
|
||||||
|
|
||||||
|
def set_unread_conversation_count(%User{local: true} = user) do
|
||||||
|
unread_query = Participation.unread_conversation_count_for_user(user)
|
||||||
|
|
||||||
|
User
|
||||||
|
|> join(:inner, [u], p in subquery(unread_query))
|
||||||
|
|> update([u, p],
|
||||||
|
set: [
|
||||||
|
info:
|
||||||
|
fragment(
|
||||||
|
"jsonb_set(?, '{unread_conversation_count}', ?::varchar::jsonb, true)",
|
||||||
|
u.info,
|
||||||
|
p.count
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|> where([u], u.id == ^user.id)
|
||||||
|
|> select([u], u)
|
||||||
|
|> Repo.update_all([])
|
||||||
|
|> case do
|
||||||
|
{1, [user]} -> set_cache(user)
|
||||||
|
_ -> {:error, user}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_unread_conversation_count(_), do: :noop
|
||||||
|
|
||||||
|
def increment_unread_conversation_count(conversation, %User{local: true} = user) do
|
||||||
|
unread_query =
|
||||||
|
Participation.unread_conversation_count_for_user(user)
|
||||||
|
|> where([p], p.conversation_id == ^conversation.id)
|
||||||
|
|
||||||
|
User
|
||||||
|
|> join(:inner, [u], p in subquery(unread_query))
|
||||||
|
|> update([u, p],
|
||||||
|
set: [
|
||||||
|
info:
|
||||||
|
fragment(
|
||||||
|
"jsonb_set(?, '{unread_conversation_count}', (coalesce((?->>'unread_conversation_count')::int, 0) + 1)::varchar::jsonb, true)",
|
||||||
|
u.info,
|
||||||
|
u.info
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|> where([u], u.id == ^user.id)
|
||||||
|
|> where([u, p], p.count == 0)
|
||||||
|
|> select([u], u)
|
||||||
|
|> Repo.update_all([])
|
||||||
|
|> case do
|
||||||
|
{1, [user]} -> set_cache(user)
|
||||||
|
_ -> {:error, user}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def increment_unread_conversation_count(_, _), do: :noop
|
||||||
|
|
||||||
def remove_duplicated_following(%User{following: following} = user) do
|
def remove_duplicated_following(%User{following: following} = user) do
|
||||||
uniq_following = Enum.uniq(following)
|
uniq_following = Enum.uniq(following)
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ defmodule Pleroma.User.Info do
|
||||||
field(:hide_followers, :boolean, default: false)
|
field(:hide_followers, :boolean, default: false)
|
||||||
field(:hide_follows, :boolean, default: false)
|
field(:hide_follows, :boolean, default: false)
|
||||||
field(:hide_favorites, :boolean, default: true)
|
field(:hide_favorites, :boolean, default: true)
|
||||||
|
field(:unread_conversation_count, :integer, default: 0)
|
||||||
field(:pinned_activities, {:array, :string}, default: [])
|
field(:pinned_activities, {:array, :string}, default: [])
|
||||||
field(:email_notifications, :map, default: %{"digest" => false})
|
field(:email_notifications, :map, default: %{"digest" => false})
|
||||||
field(:mascot, :map, default: nil)
|
field(:mascot, :map, default: nil)
|
||||||
|
|
|
@ -17,6 +17,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.MRF
|
alias Pleroma.Web.ActivityPub.MRF
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.Streamer
|
alias Pleroma.Web.Streamer
|
||||||
alias Pleroma.Web.WebFinger
|
alias Pleroma.Web.WebFinger
|
||||||
alias Pleroma.Workers.BackgroundWorker
|
alias Pleroma.Workers.BackgroundWorker
|
||||||
|
@ -291,8 +292,8 @@ def reject(%{to: to, actor: actor, object: object} = params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
||||||
# only accept false as false value
|
|
||||||
local = !(params[:local] == false)
|
local = !(params[:local] == false)
|
||||||
|
activity_id = params[:activity_id]
|
||||||
|
|
||||||
with data <- %{
|
with data <- %{
|
||||||
"to" => to,
|
"to" => to,
|
||||||
|
@ -301,6 +302,7 @@ def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
||||||
"actor" => actor,
|
"actor" => actor,
|
||||||
"object" => object
|
"object" => object
|
||||||
},
|
},
|
||||||
|
data <- Utils.maybe_put(data, "id", activity_id),
|
||||||
{:ok, activity} <- insert(data, local),
|
{:ok, activity} <- insert(data, local),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
|
|
@ -601,7 +601,7 @@ def handle_incoming(
|
||||||
) do
|
) do
|
||||||
with actor <- Containment.get_actor(data),
|
with actor <- Containment.get_actor(data),
|
||||||
{:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
|
{:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
|
||||||
{:ok, object} <- get_obj_helper(object_id),
|
{:ok, object} <- get_embedded_obj_helper(object_id, actor),
|
||||||
public <- Visibility.is_public?(data),
|
public <- Visibility.is_public?(data),
|
||||||
{:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do
|
{:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
@ -642,7 +642,8 @@ def handle_incoming(
|
||||||
to: data["to"] || [],
|
to: data["to"] || [],
|
||||||
cc: data["cc"] || [],
|
cc: data["cc"] || [],
|
||||||
object: object,
|
object: object,
|
||||||
actor: actor_id
|
actor: actor_id,
|
||||||
|
activity_id: data["id"]
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
e ->
|
e ->
|
||||||
|
@ -824,6 +825,29 @@ def get_obj_helper(id, options \\ []) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec get_embedded_obj_helper(String.t() | Object.t(), User.t()) :: {:ok, Object.t()} | nil
|
||||||
|
def get_embedded_obj_helper(%{"attributedTo" => attributed_to, "id" => object_id} = data, %User{
|
||||||
|
ap_id: ap_id
|
||||||
|
})
|
||||||
|
when attributed_to == ap_id do
|
||||||
|
with {:ok, activity} <-
|
||||||
|
handle_incoming(%{
|
||||||
|
"type" => "Create",
|
||||||
|
"to" => data["to"],
|
||||||
|
"cc" => data["cc"],
|
||||||
|
"actor" => attributed_to,
|
||||||
|
"object" => data
|
||||||
|
}) do
|
||||||
|
{:ok, Object.normalize(activity)}
|
||||||
|
else
|
||||||
|
_ -> get_obj_helper(object_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_embedded_obj_helper(object_id, _) do
|
||||||
|
get_obj_helper(object_id)
|
||||||
|
end
|
||||||
|
|
||||||
def set_reply_to_uri(%{"inReplyTo" => in_reply_to} = object) when is_binary(in_reply_to) do
|
def set_reply_to_uri(%{"inReplyTo" => in_reply_to} = object) when is_binary(in_reply_to) do
|
||||||
with false <- String.starts_with?(in_reply_to, "http"),
|
with false <- String.starts_with?(in_reply_to, "http"),
|
||||||
{:ok, %{data: replied_to_object}} <- get_obj_helper(in_reply_to) do
|
{:ok, %{data: replied_to_object}} <- get_obj_helper(in_reply_to) do
|
||||||
|
|
|
@ -830,6 +830,6 @@ def get_existing_votes(actor, %{data: %{"id" => id}}) do
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_put(map, _key, nil), do: map
|
def maybe_put(map, _key, nil), do: map
|
||||||
defp maybe_put(map, key, value), do: Map.put(map, key, value)
|
def maybe_put(map, key, value), do: Map.put(map, key, value)
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,8 @@ defmodule Pleroma.Web.CommonAPI do
|
||||||
import Pleroma.Web.Gettext
|
import Pleroma.Web.Gettext
|
||||||
import Pleroma.Web.CommonAPI.Utils
|
import Pleroma.Web.CommonAPI.Utils
|
||||||
|
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
def follow(follower, followed) do
|
def follow(follower, followed) do
|
||||||
timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout])
|
timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout])
|
||||||
|
|
||||||
|
@ -290,7 +292,7 @@ def update(user) do
|
||||||
|
|
||||||
ActivityPub.update(%{
|
ActivityPub.update(%{
|
||||||
local: true,
|
local: true,
|
||||||
to: [user.follower_address],
|
to: [Pleroma.Constants.as_public(), user.follower_address],
|
||||||
cc: [],
|
cc: [],
|
||||||
actor: user.ap_id,
|
actor: user.ap_id,
|
||||||
object: Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
|
object: Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
|
||||||
|
|
|
@ -105,6 +105,17 @@ def update_credentials(%{assigns: %{user: original_user}} = conn, params) do
|
||||||
|> Enum.concat(Emoji.Formatter.get_emoji_map(emojis_text))
|
|> Enum.concat(Emoji.Formatter.get_emoji_map(emojis_text))
|
||||||
|> Enum.dedup()
|
|> Enum.dedup()
|
||||||
|
|
||||||
|
params =
|
||||||
|
if Map.has_key?(params, "fields_attributes") do
|
||||||
|
Map.update!(params, "fields_attributes", fn fields ->
|
||||||
|
fields
|
||||||
|
|> normalize_fields_attributes()
|
||||||
|
|> Enum.filter(fn %{"name" => n} -> n != "" end)
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
params
|
||||||
|
end
|
||||||
|
|
||||||
info_params =
|
info_params =
|
||||||
[
|
[
|
||||||
:no_rich_text,
|
:no_rich_text,
|
||||||
|
@ -122,12 +133,12 @@ def update_credentials(%{assigns: %{user: original_user}} = conn, params) do
|
||||||
add_if_present(acc, params, to_string(key), key, &{:ok, truthy_param?(&1)})
|
add_if_present(acc, params, to_string(key), key, &{:ok, truthy_param?(&1)})
|
||||||
end)
|
end)
|
||||||
|> add_if_present(params, "default_scope", :default_scope)
|
|> add_if_present(params, "default_scope", :default_scope)
|
||||||
|> add_if_present(params, "fields", :fields, fn fields ->
|
|> add_if_present(params, "fields_attributes", :fields, fn fields ->
|
||||||
fields = Enum.map(fields, fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end)
|
fields = Enum.map(fields, fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end)
|
||||||
|
|
||||||
{:ok, fields}
|
{:ok, fields}
|
||||||
end)
|
end)
|
||||||
|> add_if_present(params, "fields", :raw_fields)
|
|> add_if_present(params, "fields_attributes", :raw_fields)
|
||||||
|> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store, fn value ->
|
|> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store, fn value ->
|
||||||
{:ok, Map.merge(user.info.pleroma_settings_store, value)}
|
{:ok, Map.merge(user.info.pleroma_settings_store, value)}
|
||||||
end)
|
end)
|
||||||
|
@ -168,6 +179,14 @@ defp add_if_present(map, params, params_field, map_field, value_function \\ &{:o
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp normalize_fields_attributes(fields) do
|
||||||
|
if Enum.all?(fields, &is_tuple/1) do
|
||||||
|
Enum.map(fields, fn {_, v} -> v end)
|
||||||
|
else
|
||||||
|
fields
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/accounts/relationships"
|
@doc "GET /api/v1/accounts/relationships"
|
||||||
def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
targets = User.get_all_by_ids(List.wrap(id))
|
targets = User.get_all_by_ids(List.wrap(id))
|
||||||
|
@ -301,4 +320,26 @@ def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do
|
||||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc "POST /api/v1/follows"
|
||||||
|
def follows(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
|
||||||
|
with {_, %User{} = followed} <- {:followed, User.get_cached_by_nickname(uri)},
|
||||||
|
{_, true} <- {:followed, follower.id != followed.id},
|
||||||
|
{:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do
|
||||||
|
render(conn, "show.json", user: followed, for: follower)
|
||||||
|
else
|
||||||
|
{:followed, _} -> {:error, :not_found}
|
||||||
|
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "GET /api/v1/mutes"
|
||||||
|
def mutes(%{assigns: %{user: user}} = conn, _) do
|
||||||
|
render(conn, "index.json", users: User.muted_users(user), for: user, as: :user)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "GET /api/v1/blocks"
|
||||||
|
def blocks(%{assigns: %{user: user}} = conn, _) do
|
||||||
|
render(conn, "index.json", users: User.blocked_users(user), for: user, as: :user)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,86 +5,10 @@
|
||||||
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
|
|
||||||
|
|
||||||
alias Pleroma.Bookmark
|
|
||||||
alias Pleroma.Pagination
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
|
||||||
alias Pleroma.Web.CommonAPI
|
|
||||||
alias Pleroma.Web.MastodonAPI.AccountView
|
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
def follows(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
|
|
||||||
with {_, %User{} = followed} <- {:followed, User.get_cached_by_nickname(uri)},
|
|
||||||
{_, true} <- {:followed, follower.id != followed.id},
|
|
||||||
{:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do
|
|
||||||
conn
|
|
||||||
|> put_view(AccountView)
|
|
||||||
|> render("show.json", %{user: followed, for: follower})
|
|
||||||
else
|
|
||||||
{:followed, _} ->
|
|
||||||
{:error, :not_found}
|
|
||||||
|
|
||||||
{:error, message} ->
|
|
||||||
conn
|
|
||||||
|> put_status(:forbidden)
|
|
||||||
|> json(%{error: message})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def mutes(%{assigns: %{user: user}} = conn, _) do
|
|
||||||
with muted_accounts <- User.muted_users(user) do
|
|
||||||
res = AccountView.render("index.json", users: muted_accounts, for: user, as: :user)
|
|
||||||
json(conn, res)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def blocks(%{assigns: %{user: user}} = conn, _) do
|
|
||||||
with blocked_accounts <- User.blocked_users(user) do
|
|
||||||
res = AccountView.render("index.json", users: blocked_accounts, for: user, as: :user)
|
|
||||||
json(conn, res)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def favourites(%{assigns: %{user: user}} = conn, params) do
|
|
||||||
params =
|
|
||||||
params
|
|
||||||
|> Map.put("type", "Create")
|
|
||||||
|> Map.put("favorited_by", user.ap_id)
|
|
||||||
|> Map.put("blocking_user", user)
|
|
||||||
|
|
||||||
activities =
|
|
||||||
ActivityPub.fetch_activities([], params)
|
|
||||||
|> Enum.reverse()
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> add_link_headers(activities)
|
|
||||||
|> put_view(StatusView)
|
|
||||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
|
||||||
end
|
|
||||||
|
|
||||||
def bookmarks(%{assigns: %{user: user}} = conn, params) do
|
|
||||||
user = User.get_cached_by_id(user.id)
|
|
||||||
|
|
||||||
bookmarks =
|
|
||||||
Bookmark.for_user_query(user.id)
|
|
||||||
|> Pagination.fetch_paginated(params)
|
|
||||||
|
|
||||||
activities =
|
|
||||||
bookmarks
|
|
||||||
|> Enum.map(fn b -> Map.put(b.activity, :bookmark, Map.delete(b, :activity)) end)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> add_link_headers(bookmarks)
|
|
||||||
|> put_view(StatusView)
|
|
||||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
|
||||||
end
|
|
||||||
|
|
||||||
# Stubs for unimplemented mastodon api
|
# Stubs for unimplemented mastodon api
|
||||||
#
|
#
|
||||||
def empty_array(conn, _) do
|
def empty_array(conn, _) do
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
defmodule Pleroma.Web.MastodonAPI.StatusController do
|
defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [try_render: 3]
|
import Pleroma.Web.ControllerHelper, only: [try_render: 3, add_link_headers: 2]
|
||||||
|
|
||||||
require Ecto.Query
|
require Ecto.Query
|
||||||
|
|
||||||
|
@ -283,4 +283,39 @@ def context(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
render(conn, "context.json", activity: activity, activities: activities, user: user)
|
render(conn, "context.json", activity: activity, activities: activities, user: user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc "GET /api/v1/favourites"
|
||||||
|
def favourites(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
params =
|
||||||
|
params
|
||||||
|
|> Map.put("type", "Create")
|
||||||
|
|> Map.put("favorited_by", user.ap_id)
|
||||||
|
|> Map.put("blocking_user", user)
|
||||||
|
|
||||||
|
activities =
|
||||||
|
ActivityPub.fetch_activities([], params)
|
||||||
|
|> Enum.reverse()
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> add_link_headers(activities)
|
||||||
|
|> render("index.json", activities: activities, for: user, as: :activity)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "GET /api/v1/bookmarks"
|
||||||
|
def bookmarks(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
|
bookmarks =
|
||||||
|
user.id
|
||||||
|
|> Bookmark.for_user_query()
|
||||||
|
|> Pleroma.Pagination.fetch_paginated(params)
|
||||||
|
|
||||||
|
activities =
|
||||||
|
bookmarks
|
||||||
|
|> Enum.map(fn b -> Map.put(b.activity, :bookmark, Map.delete(b, :activity)) end)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> add_link_headers(bookmarks)
|
||||||
|
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -167,6 +167,7 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
|> maybe_put_chat_token(user, opts[:for], opts)
|
|> maybe_put_chat_token(user, opts[:for], opts)
|
||||||
|> maybe_put_activation_status(user, opts[:for])
|
|> maybe_put_activation_status(user, opts[:for])
|
||||||
|> maybe_put_follow_requests_count(user, opts[:for])
|
|> maybe_put_follow_requests_count(user, opts[:for])
|
||||||
|
|> maybe_put_unread_conversation_count(user, opts[:for])
|
||||||
end
|
end
|
||||||
|
|
||||||
defp username_from_nickname(string) when is_binary(string) do
|
defp username_from_nickname(string) when is_binary(string) do
|
||||||
|
@ -248,6 +249,16 @@ defp maybe_put_activation_status(data, user, %User{info: %{is_admin: true}}) do
|
||||||
|
|
||||||
defp maybe_put_activation_status(data, _, _), do: data
|
defp maybe_put_activation_status(data, _, _), do: data
|
||||||
|
|
||||||
|
defp maybe_put_unread_conversation_count(data, %User{id: user_id} = user, %User{id: user_id}) do
|
||||||
|
data
|
||||||
|
|> Kernel.put_in(
|
||||||
|
[:pleroma, :unread_conversation_count],
|
||||||
|
user.info.unread_conversation_count
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_put_unread_conversation_count(data, _, _), do: data
|
||||||
|
|
||||||
defp image_url(%{"url" => [%{"href" => href} | _]}), do: href
|
defp image_url(%{"url" => [%{"href" => href} | _]}), do: href
|
||||||
defp image_url(_), do: nil
|
defp image_url(_), do: nil
|
||||||
end
|
end
|
||||||
|
|
|
@ -355,14 +355,14 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array)
|
get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array)
|
||||||
|
|
||||||
get("/follow_requests", FollowRequestController, :index)
|
get("/follow_requests", FollowRequestController, :index)
|
||||||
get("/blocks", MastodonAPIController, :blocks)
|
get("/blocks", AccountController, :blocks)
|
||||||
get("/mutes", MastodonAPIController, :mutes)
|
get("/mutes", AccountController, :mutes)
|
||||||
|
|
||||||
get("/timelines/home", TimelineController, :home)
|
get("/timelines/home", TimelineController, :home)
|
||||||
get("/timelines/direct", TimelineController, :direct)
|
get("/timelines/direct", TimelineController, :direct)
|
||||||
|
|
||||||
get("/favourites", MastodonAPIController, :favourites)
|
get("/favourites", StatusController, :favourites)
|
||||||
get("/bookmarks", MastodonAPIController, :bookmarks)
|
get("/bookmarks", StatusController, :bookmarks)
|
||||||
|
|
||||||
get("/notifications", NotificationController, :index)
|
get("/notifications", NotificationController, :index)
|
||||||
get("/notifications/:id", NotificationController, :show)
|
get("/notifications/:id", NotificationController, :show)
|
||||||
|
@ -434,7 +434,7 @@ defmodule Pleroma.Web.Router do
|
||||||
scope [] do
|
scope [] do
|
||||||
pipe_through(:oauth_follow)
|
pipe_through(:oauth_follow)
|
||||||
|
|
||||||
post("/follows", MastodonAPIController, :follows)
|
post("/follows", AccountController, :follows)
|
||||||
post("/accounts/:id/follow", AccountController, :follow)
|
post("/accounts/:id/follow", AccountController, :follow)
|
||||||
post("/accounts/:id/unfollow", AccountController, :unfollow)
|
post("/accounts/:id/unfollow", AccountController, :unfollow)
|
||||||
post("/accounts/:id/block", AccountController, :block)
|
post("/accounts/:id/block", AccountController, :block)
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddUnreadConversationCountToUserInfo do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def up do
|
||||||
|
execute("""
|
||||||
|
update users set info = jsonb_set(info, '{unread_conversation_count}', 0::varchar::jsonb, true) where local=true
|
||||||
|
""")
|
||||||
|
end
|
||||||
|
|
||||||
|
def down, do: :ok
|
||||||
|
end
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Conversation.ParticipationTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
alias Pleroma.Conversation.Participation
|
alias Pleroma.Conversation.Participation
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
test "getting a participation will also preload things" do
|
test "getting a participation will also preload things" do
|
||||||
|
@ -30,6 +31,8 @@ test "for a new conversation, it sets the recipents of the participation" do
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"})
|
CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"})
|
||||||
|
|
||||||
|
user = User.get_cached_by_id(user.id)
|
||||||
|
other_user = User.get_cached_by_id(user.id)
|
||||||
[participation] = Participation.for_user(user)
|
[participation] = Participation.for_user(user)
|
||||||
participation = Pleroma.Repo.preload(participation, :recipients)
|
participation = Pleroma.Repo.preload(participation, :recipients)
|
||||||
|
|
||||||
|
@ -155,6 +158,7 @@ test "it sets recipients, always keeping the owner of the participation even whe
|
||||||
[participation] = Participation.for_user_with_last_activity_id(user)
|
[participation] = Participation.for_user_with_last_activity_id(user)
|
||||||
|
|
||||||
participation = Repo.preload(participation, :recipients)
|
participation = Repo.preload(participation, :recipients)
|
||||||
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
assert participation.recipients |> length() == 1
|
assert participation.recipients |> length() == 1
|
||||||
assert user in participation.recipients
|
assert user in participation.recipients
|
||||||
|
|
43
test/fixtures/bogus-mastodon-announce.json
vendored
Normal file
43
test/fixtures/bogus-mastodon-announce.json
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"type": "Announce",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"published": "2018-02-17T19:39:15Z",
|
||||||
|
"object": {
|
||||||
|
"type": "Note",
|
||||||
|
"id": "https://mastodon.social/users/emelie/statuses/101849165031453404",
|
||||||
|
"attributedTo": "https://mastodon.social/users/emelie",
|
||||||
|
"content": "this is a public toot",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"cc": [
|
||||||
|
"https://mastodon.social/users/emelie",
|
||||||
|
"https://mastodon.social/users/emelie/followers"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"id": "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity",
|
||||||
|
"cc": [
|
||||||
|
"http://mastodon.example.org/users/admin",
|
||||||
|
"http://mastodon.example.org/users/admin/followers"
|
||||||
|
],
|
||||||
|
"atomUri": "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity",
|
||||||
|
"actor": "http://mastodon.example.org/users/admin",
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
{
|
||||||
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
|
"sensitive": "as:sensitive",
|
||||||
|
"ostatus": "http://ostatus.org#",
|
||||||
|
"movedTo": "as:movedTo",
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||||
|
"conversation": "ostatus:conversation",
|
||||||
|
"atomUri": "ostatus:atomUri",
|
||||||
|
"Hashtag": "as:Hashtag",
|
||||||
|
"Emoji": "toot:Emoji"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
35
test/fixtures/mastodon-announce-private.json
vendored
Normal file
35
test/fixtures/mastodon-announce-private.json
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"type": "Announce",
|
||||||
|
"to": [
|
||||||
|
"http://mastodon.example.org/users/admin/followers"
|
||||||
|
],
|
||||||
|
"published": "2018-02-17T19:39:15Z",
|
||||||
|
"object": {
|
||||||
|
"type": "Note",
|
||||||
|
"id": "http://mastodon.example.org/@admin/99541947525187368",
|
||||||
|
"attributedTo": "http://mastodon.example.org/users/admin",
|
||||||
|
"content": "this is a private toot",
|
||||||
|
"to": [
|
||||||
|
"http://mastodon.example.org/users/admin/followers"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"id": "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity",
|
||||||
|
"atomUri": "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity",
|
||||||
|
"actor": "http://mastodon.example.org/users/admin",
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
{
|
||||||
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
|
"sensitive": "as:sensitive",
|
||||||
|
"ostatus": "http://ostatus.org#",
|
||||||
|
"movedTo": "as:movedTo",
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||||
|
"conversation": "ostatus:conversation",
|
||||||
|
"atomUri": "ostatus:atomUri",
|
||||||
|
"Hashtag": "as:Hashtag",
|
||||||
|
"Emoji": "toot:Emoji"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -46,6 +46,14 @@ def get("https://mastodon.social/users/emelie/statuses/101849165031453009", _, _
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get("https://mastodon.social/users/emelie/statuses/101849165031453404", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 404,
|
||||||
|
body: ""
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
def get("https://mastodon.social/users/emelie", _, _, _) do
|
def get("https://mastodon.social/users/emelie", _, _, _) do
|
||||||
{:ok,
|
{:ok,
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
|
@ -349,6 +357,14 @@ def get(
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get("http://mastodon.example.org/@admin/99541947525187368", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 404,
|
||||||
|
body: ""
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
def get("https://shitposter.club/notice/7369654", _, _, _) do
|
def get("https://shitposter.club/notice/7369654", _, _, _) do
|
||||||
{:ok,
|
{:ok,
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
|
|
|
@ -479,6 +479,33 @@ test "it works for incoming announces with an existing activity" do
|
||||||
assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id
|
assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it works for incoming announces with an inlined activity" do
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/mastodon-announce-private.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||||
|
assert data["type"] == "Announce"
|
||||||
|
|
||||||
|
assert data["id"] ==
|
||||||
|
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
|
||||||
|
|
||||||
|
object = Object.normalize(data["object"])
|
||||||
|
|
||||||
|
assert object.data["id"] == "http://mastodon.example.org/@admin/99541947525187368"
|
||||||
|
assert object.data["content"] == "this is a private toot"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it rejects incoming announces with an inlined activity from another origin" do
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/bogus-mastodon-announce.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|
||||||
|
assert :error = Transmogrifier.handle_incoming(data)
|
||||||
|
end
|
||||||
|
|
||||||
test "it does not clobber the addressing on announce activities" do
|
test "it does not clobber the addressing on announce activities" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
|
||||||
|
@ -597,6 +624,8 @@ test "it works for incoming update activities" do
|
||||||
|
|
||||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
|
||||||
|
|
||||||
|
assert data["id"] == update_data["id"]
|
||||||
|
|
||||||
user = User.get_cached_by_ap_id(data["actor"])
|
user = User.get_cached_by_ap_id(data["actor"])
|
||||||
assert user.name == "gargle"
|
assert user.name == "gargle"
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ defmodule Pleroma.Web.CommonAPITest do
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
clear_config([:instance, :safe_dm_mentions])
|
clear_config([:instance, :safe_dm_mentions])
|
||||||
clear_config([:instance, :limit])
|
clear_config([:instance, :limit])
|
||||||
clear_config([:instance, :max_pinned_statuses])
|
clear_config([:instance, :max_pinned_statuses])
|
||||||
|
@ -96,11 +98,13 @@ test "it adds emoji in the object" do
|
||||||
test "it adds emoji when updating profiles" do
|
test "it adds emoji when updating profiles" do
|
||||||
user = insert(:user, %{name: ":firefox:"})
|
user = insert(:user, %{name: ":firefox:"})
|
||||||
|
|
||||||
CommonAPI.update(user)
|
{:ok, activity} = CommonAPI.update(user)
|
||||||
user = User.get_cached_by_ap_id(user.ap_id)
|
user = User.get_cached_by_ap_id(user.ap_id)
|
||||||
[firefox] = user.info.source_data["tag"]
|
[firefox] = user.info.source_data["tag"]
|
||||||
|
|
||||||
assert firefox["name"] == ":firefox:"
|
assert firefox["name"] == ":firefox:"
|
||||||
|
|
||||||
|
assert Pleroma.Constants.as_public() in activity.recipients
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "posting" do
|
describe "posting" do
|
||||||
|
|
|
@ -328,7 +328,7 @@ test "update fields", %{conn: conn} do
|
||||||
account =
|
account =
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{"fields" => fields})
|
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|
||||||
|> json_response(200)
|
|> json_response(200)
|
||||||
|
|
||||||
assert account["fields"] == [
|
assert account["fields"] == [
|
||||||
|
@ -344,6 +344,35 @@ test "update fields", %{conn: conn} do
|
||||||
%{"name" => "link", "value" => "cofe.io"}
|
%{"name" => "link", "value" => "cofe.io"}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
fields =
|
||||||
|
[
|
||||||
|
"fields_attributes[1][name]=link",
|
||||||
|
"fields_attributes[1][value]=cofe.io",
|
||||||
|
"fields_attributes[0][name]=<a href=\"http://google.com\">foo</a>",
|
||||||
|
"fields_attributes[0][value]=bar"
|
||||||
|
]
|
||||||
|
|> Enum.join("&")
|
||||||
|
|
||||||
|
account =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/x-www-form-urlencoded")
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", fields)
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert account["fields"] == [
|
||||||
|
%{"name" => "foo", "value" => "bar"},
|
||||||
|
%{"name" => "link", "value" => ~S(<a href="http://cofe.io" rel="ugc">cofe.io</a>)}
|
||||||
|
]
|
||||||
|
|
||||||
|
assert account["source"]["fields"] == [
|
||||||
|
%{
|
||||||
|
"name" => "<a href=\"http://google.com\">foo</a>",
|
||||||
|
"value" => "bar"
|
||||||
|
},
|
||||||
|
%{"name" => "link", "value" => "cofe.io"}
|
||||||
|
]
|
||||||
|
|
||||||
name_limit = Pleroma.Config.get([:instance, :account_field_name_length])
|
name_limit = Pleroma.Config.get([:instance, :account_field_name_length])
|
||||||
value_limit = Pleroma.Config.get([:instance, :account_field_value_length])
|
value_limit = Pleroma.Config.get([:instance, :account_field_value_length])
|
||||||
|
|
||||||
|
@ -354,7 +383,7 @@ test "update fields", %{conn: conn} do
|
||||||
assert %{"error" => "Invalid request"} ==
|
assert %{"error" => "Invalid request"} ==
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{"fields" => fields})
|
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|
||||||
|> json_response(403)
|
|> json_response(403)
|
||||||
|
|
||||||
long_name = Enum.map(0..name_limit, fn _ -> "x" end) |> Enum.join()
|
long_name = Enum.map(0..name_limit, fn _ -> "x" end) |> Enum.join()
|
||||||
|
@ -364,7 +393,7 @@ test "update fields", %{conn: conn} do
|
||||||
assert %{"error" => "Invalid request"} ==
|
assert %{"error" => "Invalid request"} ==
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{"fields" => fields})
|
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|
||||||
|> json_response(403)
|
|> json_response(403)
|
||||||
|
|
||||||
Pleroma.Config.put([:instance, :max_account_fields], 1)
|
Pleroma.Config.put([:instance, :max_account_fields], 1)
|
||||||
|
@ -377,8 +406,23 @@ test "update fields", %{conn: conn} do
|
||||||
assert %{"error" => "Invalid request"} ==
|
assert %{"error" => "Invalid request"} ==
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{"fields" => fields})
|
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|
||||||
|> json_response(403)
|
|> json_response(403)
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
%{"name" => "foo", "value" => ""},
|
||||||
|
%{"name" => "", "value" => "bar"}
|
||||||
|
]
|
||||||
|
|
||||||
|
account =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert account["fields"] == [
|
||||||
|
%{"name" => "foo", "value" => ""}
|
||||||
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -849,4 +849,34 @@ test "returns an empty list on a bad request", %{conn: conn} do
|
||||||
assert [] = json_response(conn, 200)
|
assert [] = json_response(conn, 200)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "getting a list of mutes", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, user} = User.mute(user, other_user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/mutes")
|
||||||
|
|
||||||
|
other_user_id = to_string(other_user.id)
|
||||||
|
assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "getting a list of blocks", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, user} = User.block(user, other_user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/blocks")
|
||||||
|
|
||||||
|
other_user_id = to_string(other_user.id)
|
||||||
|
assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,19 +10,23 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
test "Conversations", %{conn: conn} do
|
test "returns a list of conversations", %{conn: conn} do
|
||||||
user_one = insert(:user)
|
user_one = insert(:user)
|
||||||
user_two = insert(:user)
|
user_two = insert(:user)
|
||||||
user_three = insert(:user)
|
user_three = insert(:user)
|
||||||
|
|
||||||
{:ok, user_two} = User.follow(user_two, user_one)
|
{:ok, user_two} = User.follow(user_two, user_one)
|
||||||
|
|
||||||
|
assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 0
|
||||||
|
|
||||||
{:ok, direct} =
|
{:ok, direct} =
|
||||||
CommonAPI.post(user_one, %{
|
CommonAPI.post(user_one, %{
|
||||||
"status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
|
"status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
|
||||||
"visibility" => "direct"
|
"visibility" => "direct"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 1
|
||||||
|
|
||||||
{:ok, _follower_only} =
|
{:ok, _follower_only} =
|
||||||
CommonAPI.post(user_one, %{
|
CommonAPI.post(user_one, %{
|
||||||
"status" => "Hi @#{user_two.nickname}!",
|
"status" => "Hi @#{user_two.nickname}!",
|
||||||
|
@ -52,23 +56,100 @@ test "Conversations", %{conn: conn} do
|
||||||
assert is_binary(res_id)
|
assert is_binary(res_id)
|
||||||
assert unread == true
|
assert unread == true
|
||||||
assert res_last_status["id"] == direct.id
|
assert res_last_status["id"] == direct.id
|
||||||
|
assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the last_status on reply", %{conn: conn} do
|
||||||
|
user_one = insert(:user)
|
||||||
|
user_two = insert(:user)
|
||||||
|
|
||||||
|
{:ok, direct} =
|
||||||
|
CommonAPI.post(user_one, %{
|
||||||
|
"status" => "Hi @#{user_two.nickname}",
|
||||||
|
"visibility" => "direct"
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, direct_reply} =
|
||||||
|
CommonAPI.post(user_two, %{
|
||||||
|
"status" => "reply",
|
||||||
|
"visibility" => "direct",
|
||||||
|
"in_reply_to_status_id" => direct.id
|
||||||
|
})
|
||||||
|
|
||||||
|
[%{"last_status" => res_last_status}] =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user_one)
|
||||||
|
|> get("/api/v1/conversations")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert res_last_status["id"] == direct_reply.id
|
||||||
|
end
|
||||||
|
|
||||||
|
test "the user marks a conversation as read", %{conn: conn} do
|
||||||
|
user_one = insert(:user)
|
||||||
|
user_two = insert(:user)
|
||||||
|
|
||||||
|
{:ok, direct} =
|
||||||
|
CommonAPI.post(user_one, %{
|
||||||
|
"status" => "Hi @#{user_two.nickname}",
|
||||||
|
"visibility" => "direct"
|
||||||
|
})
|
||||||
|
|
||||||
|
[%{"id" => direct_conversation_id, "unread" => true}] =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user_one)
|
||||||
|
|> get("/api/v1/conversations")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
%{"unread" => false} =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user_one)
|
||||||
|
|> post("/api/v1/conversations/#{direct_conversation_id}/read")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 0
|
||||||
|
|
||||||
|
# The conversation is marked as unread on reply
|
||||||
|
{:ok, _} =
|
||||||
|
CommonAPI.post(user_two, %{
|
||||||
|
"status" => "reply",
|
||||||
|
"visibility" => "direct",
|
||||||
|
"in_reply_to_status_id" => direct.id
|
||||||
|
})
|
||||||
|
|
||||||
|
[%{"unread" => true}] =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user_one)
|
||||||
|
|> get("/api/v1/conversations")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1
|
||||||
|
|
||||||
|
# A reply doesn't increment the user's unread_conversation_count if the conversation is unread
|
||||||
|
{:ok, _} =
|
||||||
|
CommonAPI.post(user_two, %{
|
||||||
|
"status" => "reply",
|
||||||
|
"visibility" => "direct",
|
||||||
|
"in_reply_to_status_id" => direct.id
|
||||||
|
})
|
||||||
|
|
||||||
|
assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "(vanilla) Mastodon frontend behaviour", %{conn: conn} do
|
||||||
|
user_one = insert(:user)
|
||||||
|
user_two = insert(:user)
|
||||||
|
|
||||||
|
{:ok, direct} =
|
||||||
|
CommonAPI.post(user_one, %{
|
||||||
|
"status" => "Hi @#{user_two.nickname}!",
|
||||||
|
"visibility" => "direct"
|
||||||
|
})
|
||||||
|
|
||||||
# Apparently undocumented API endpoint
|
|
||||||
res_conn =
|
res_conn =
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user_one)
|
|> assign(:user, user_one)
|
||||||
|> post("/api/v1/conversations/#{res_id}/read")
|
|> get("/api/v1/statuses/#{direct.id}/context")
|
||||||
|
|
||||||
assert response = json_response(res_conn, 200)
|
|
||||||
assert length(response["accounts"]) == 2
|
|
||||||
assert response["last_status"]["id"] == direct.id
|
|
||||||
assert response["unread"] == false
|
|
||||||
|
|
||||||
# (vanilla) Mastodon frontend behaviour
|
|
||||||
res_conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user_one)
|
|
||||||
|> get("/api/v1/statuses/#{res_last_status["id"]}/context")
|
|
||||||
|
|
||||||
assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
|
assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1242,4 +1242,51 @@ test "context" do
|
||||||
"descendants" => [%{"id" => ^id4}, %{"id" => ^id5}]
|
"descendants" => [%{"id" => ^id4}, %{"id" => ^id5}]
|
||||||
} = response
|
} = response
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns the favorites of a user", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
|
||||||
|
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
|
||||||
|
|
||||||
|
{:ok, _, _} = CommonAPI.favorite(activity.id, user)
|
||||||
|
|
||||||
|
first_conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/favourites")
|
||||||
|
|
||||||
|
assert [status] = json_response(first_conn, 200)
|
||||||
|
assert status["id"] == to_string(activity.id)
|
||||||
|
|
||||||
|
assert [{"link", _link_header}] =
|
||||||
|
Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
|
||||||
|
|
||||||
|
# Honours query params
|
||||||
|
{:ok, second_activity} =
|
||||||
|
CommonAPI.post(other_user, %{
|
||||||
|
"status" =>
|
||||||
|
"Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
|
||||||
|
|
||||||
|
last_like = status["id"]
|
||||||
|
|
||||||
|
second_conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/favourites?since_id=#{last_like}")
|
||||||
|
|
||||||
|
assert [second_status] = json_response(second_conn, 200)
|
||||||
|
assert second_status["id"] == to_string(second_activity.id)
|
||||||
|
|
||||||
|
third_conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/favourites?limit=0")
|
||||||
|
|
||||||
|
assert [] = json_response(third_conn, 200)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
||||||
|
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
@ -20,36 +19,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
||||||
|
|
||||||
clear_config([:rich_media, :enabled])
|
clear_config([:rich_media, :enabled])
|
||||||
|
|
||||||
test "getting a list of mutes", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
other_user = insert(:user)
|
|
||||||
|
|
||||||
{:ok, user} = User.mute(user, other_user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> get("/api/v1/mutes")
|
|
||||||
|
|
||||||
other_user_id = to_string(other_user.id)
|
|
||||||
assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "getting a list of blocks", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
other_user = insert(:user)
|
|
||||||
|
|
||||||
{:ok, user} = User.block(user, other_user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> get("/api/v1/blocks")
|
|
||||||
|
|
||||||
other_user_id = to_string(other_user.id)
|
|
||||||
assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "unimplemented follow_requests, blocks, domain blocks" do
|
test "unimplemented follow_requests, blocks, domain blocks" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
@ -64,53 +33,6 @@ test "unimplemented follow_requests, blocks, domain blocks" do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns the favorites of a user", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
other_user = insert(:user)
|
|
||||||
|
|
||||||
{:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
|
|
||||||
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
|
|
||||||
|
|
||||||
{:ok, _, _} = CommonAPI.favorite(activity.id, user)
|
|
||||||
|
|
||||||
first_conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> get("/api/v1/favourites")
|
|
||||||
|
|
||||||
assert [status] = json_response(first_conn, 200)
|
|
||||||
assert status["id"] == to_string(activity.id)
|
|
||||||
|
|
||||||
assert [{"link", _link_header}] =
|
|
||||||
Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
|
|
||||||
|
|
||||||
# Honours query params
|
|
||||||
{:ok, second_activity} =
|
|
||||||
CommonAPI.post(other_user, %{
|
|
||||||
"status" =>
|
|
||||||
"Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
|
|
||||||
})
|
|
||||||
|
|
||||||
{:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
|
|
||||||
|
|
||||||
last_like = status["id"]
|
|
||||||
|
|
||||||
second_conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> get("/api/v1/favourites?since_id=#{last_like}")
|
|
||||||
|
|
||||||
assert [second_status] = json_response(second_conn, 200)
|
|
||||||
assert second_status["id"] == to_string(second_activity.id)
|
|
||||||
|
|
||||||
third_conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> get("/api/v1/favourites?limit=0")
|
|
||||||
|
|
||||||
assert [] = json_response(third_conn, 200)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "link headers" do
|
describe "link headers" do
|
||||||
test "preserves parameters in link headers", %{conn: conn} do
|
test "preserves parameters in link headers", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
|
@ -418,6 +418,27 @@ test "shows actual follower/following count to the account owner" do
|
||||||
following_count: 1
|
following_count: 1
|
||||||
} = AccountView.render("show.json", %{user: user, for: user})
|
} = AccountView.render("show.json", %{user: user, for: user})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "shows unread_conversation_count only to the account owner" do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, _activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
"status" => "Hey @#{other_user.nickname}.",
|
||||||
|
"visibility" => "direct"
|
||||||
|
})
|
||||||
|
|
||||||
|
user = User.get_cached_by_ap_id(user.ap_id)
|
||||||
|
|
||||||
|
assert AccountView.render("show.json", %{user: user, for: other_user})[:pleroma][
|
||||||
|
:unread_conversation_count
|
||||||
|
] == nil
|
||||||
|
|
||||||
|
assert AccountView.render("show.json", %{user: user, for: user})[:pleroma][
|
||||||
|
:unread_conversation_count
|
||||||
|
] == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "follow requests counter" do
|
describe "follow requests counter" do
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
@ -133,6 +134,7 @@ test "PATCH /api/v1/pleroma/conversations/:id", %{conn: conn} do
|
||||||
|
|
||||||
participation = Repo.preload(participation, :recipients)
|
participation = Repo.preload(participation, :recipients)
|
||||||
|
|
||||||
|
user = User.get_cached_by_id(user.id)
|
||||||
assert [user] == participation.recipients
|
assert [user] == participation.recipients
|
||||||
assert other_user not in participation.recipients
|
assert other_user not in participation.recipients
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue