Removing TwitterAPI See merge request pleroma/pleroma!1605tags/v1.1.4
@@ -110,6 +110,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
- RichMedia: add the rich media ttl based on image expiration time. | |||
### Removed | |||
- GNU Social API with Qvitter extensions support | |||
- Emoji: Remove longfox emojis. | |||
- Remove `Reply-To` header from report emails for admins. | |||
- ActivityPub: The `accept_blocks` configuration setting. | |||
@@ -8,7 +8,7 @@ Pleroma is a microblogging server software that can federate (= exchange message | |||
Pleroma is written in Elixir, high-performance and can run on small devices like a Raspberry Pi. | |||
For clients it supports both the [GNU Social API with Qvitter extensions](https://twitter-api.readthedocs.io/en/latest/index.html) and the [Mastodon client API](https://docs.joinmastodon.org/api/guidelines/). | |||
For clients it supports the [Mastodon client API](https://docs.joinmastodon.org/api/guidelines/) with Pleroma extensions (see "Pleroma's APIs and Mastodon API extensions" section on <https://docs-develop.pleroma.social>). | |||
- [Client Applications for Pleroma](https://docs-develop.pleroma.social/clients.html) | |||
@@ -477,53 +477,12 @@ defmodule Pleroma.Web.Router do | |||
scope "/api", Pleroma.Web do | |||
pipe_through(:api) | |||
post("/account/register", TwitterAPI.Controller, :register) | |||
post("/account/password_reset", TwitterAPI.Controller, :password_reset) | |||
post("/account/resend_confirmation_email", TwitterAPI.Controller, :resend_confirmation_email) | |||
get( | |||
"/account/confirm_email/:user_id/:token", | |||
TwitterAPI.Controller, | |||
:confirm_email, | |||
as: :confirm_email | |||
) | |||
scope [] do | |||
pipe_through(:oauth_read_or_public) | |||
get("/statuses/user_timeline", TwitterAPI.Controller, :user_timeline) | |||
get("/qvitter/statuses/user_timeline", TwitterAPI.Controller, :user_timeline) | |||
get("/users/show", TwitterAPI.Controller, :show_user) | |||
get("/statuses/followers", TwitterAPI.Controller, :followers) | |||
get("/statuses/friends", TwitterAPI.Controller, :friends) | |||
get("/statuses/blocks", TwitterAPI.Controller, :blocks) | |||
get("/statuses/show/:id", TwitterAPI.Controller, :fetch_status) | |||
get("/statusnet/conversation/:id", TwitterAPI.Controller, :fetch_conversation) | |||
get("/search", TwitterAPI.Controller, :search) | |||
get("/statusnet/tags/timeline/:tag", TwitterAPI.Controller, :public_and_external_timeline) | |||
end | |||
end | |||
scope "/api", Pleroma.Web do | |||
pipe_through([:api, :oauth_read_or_public]) | |||
get("/statuses/public_timeline", TwitterAPI.Controller, :public_timeline) | |||
get( | |||
"/statuses/public_and_external_timeline", | |||
TwitterAPI.Controller, | |||
:public_and_external_timeline | |||
) | |||
get("/statuses/networkpublic_timeline", TwitterAPI.Controller, :public_and_external_timeline) | |||
end | |||
scope "/api", Pleroma.Web, as: :twitter_api_search do | |||
pipe_through([:api, :oauth_read_or_public]) | |||
get("/pleroma/search_user", TwitterAPI.Controller, :search_user) | |||
end | |||
scope "/api", Pleroma.Web, as: :authenticated_twitter_api do | |||
@@ -535,67 +494,8 @@ defmodule Pleroma.Web.Router do | |||
scope [] do | |||
pipe_through(:oauth_read) | |||
get("/account/verify_credentials", TwitterAPI.Controller, :verify_credentials) | |||
post("/account/verify_credentials", TwitterAPI.Controller, :verify_credentials) | |||
get("/statuses/home_timeline", TwitterAPI.Controller, :friends_timeline) | |||
get("/statuses/friends_timeline", TwitterAPI.Controller, :friends_timeline) | |||
get("/statuses/mentions", TwitterAPI.Controller, :mentions_timeline) | |||
get("/statuses/mentions_timeline", TwitterAPI.Controller, :mentions_timeline) | |||
get("/statuses/dm_timeline", TwitterAPI.Controller, :dm_timeline) | |||
get("/qvitter/statuses/notifications", TwitterAPI.Controller, :notifications) | |||
get("/pleroma/friend_requests", TwitterAPI.Controller, :friend_requests) | |||
get("/friends/ids", TwitterAPI.Controller, :friends_ids) | |||
get("/friendships/no_retweets/ids", TwitterAPI.Controller, :empty_array) | |||
get("/mutes/users/ids", TwitterAPI.Controller, :empty_array) | |||
get("/qvitter/mutes", TwitterAPI.Controller, :raw_empty_array) | |||
get("/externalprofile/show", TwitterAPI.Controller, :external_profile) | |||
post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read) | |||
end | |||
scope [] do | |||
pipe_through(:oauth_write) | |||
post("/account/update_profile", TwitterAPI.Controller, :update_profile) | |||
post("/account/update_profile_banner", TwitterAPI.Controller, :update_banner) | |||
post("/qvitter/update_background_image", TwitterAPI.Controller, :update_background) | |||
post("/statuses/update", TwitterAPI.Controller, :status_update) | |||
post("/statuses/retweet/:id", TwitterAPI.Controller, :retweet) | |||
post("/statuses/unretweet/:id", TwitterAPI.Controller, :unretweet) | |||
post("/statuses/destroy/:id", TwitterAPI.Controller, :delete_post) | |||
post("/statuses/pin/:id", TwitterAPI.Controller, :pin) | |||
post("/statuses/unpin/:id", TwitterAPI.Controller, :unpin) | |||
post("/statusnet/media/upload", TwitterAPI.Controller, :upload) | |||
post("/media/upload", TwitterAPI.Controller, :upload_json) | |||
post("/media/metadata/create", TwitterAPI.Controller, :update_media) | |||
post("/favorites/create/:id", TwitterAPI.Controller, :favorite) | |||
post("/favorites/create", TwitterAPI.Controller, :favorite) | |||
post("/favorites/destroy/:id", TwitterAPI.Controller, :unfavorite) | |||
post("/qvitter/update_avatar", TwitterAPI.Controller, :update_avatar) | |||
end | |||
scope [] do | |||
pipe_through(:oauth_follow) | |||
post("/pleroma/friendships/approve", TwitterAPI.Controller, :approve_friend_request) | |||
post("/pleroma/friendships/deny", TwitterAPI.Controller, :deny_friend_request) | |||
post("/friendships/create", TwitterAPI.Controller, :follow) | |||
post("/friendships/destroy", TwitterAPI.Controller, :unfollow) | |||
post("/blocks/create", TwitterAPI.Controller, :block) | |||
post("/blocks/destroy", TwitterAPI.Controller, :unblock) | |||
end | |||
end | |||
pipeline :ap_service_actor do | |||
@@ -1,38 +0,0 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.TwitterAPI.Representers.BaseRepresenter do | |||
defmacro __using__(_opts) do | |||
quote do | |||
def to_json(object) do | |||
to_json(object, %{}) | |||
end | |||
def to_json(object, options) do | |||
object | |||
|> to_map(options) | |||
|> Jason.encode!() | |||
end | |||
def enum_to_list(enum, options) do | |||
mapping = fn el -> to_map(el, options) end | |||
Enum.map(enum, mapping) | |||
end | |||
def to_map(object) do | |||
to_map(object, %{}) | |||
end | |||
def enum_to_json(enum) do | |||
enum_to_json(enum, %{}) | |||
end | |||
def enum_to_json(enum, options) do | |||
enum | |||
|> enum_to_list(options) | |||
|> Jason.encode!() | |||
end | |||
end | |||
end | |||
end |
@@ -1,39 +0,0 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter do | |||
use Pleroma.Web.TwitterAPI.Representers.BaseRepresenter | |||
alias Pleroma.Object | |||
def to_map(%Object{data: %{"url" => [url | _]}} = object, _opts) do | |||
data = object.data | |||
%{ | |||
url: url["href"] |> Pleroma.Web.MediaProxy.url(), | |||
mimetype: url["mediaType"] || url["mimeType"], | |||
id: data["uuid"], | |||
oembed: false, | |||
description: data["name"] | |||
} | |||
end | |||
def to_map(%Object{data: %{"url" => url} = data}, _opts) when is_binary(url) do | |||
%{ | |||
url: url |> Pleroma.Web.MediaProxy.url(), | |||
mimetype: data["mediaType"] || data["mimeType"], | |||
id: data["uuid"], | |||
oembed: false, | |||
description: data["name"] | |||
} | |||
end | |||
def to_map(%Object{}, _opts) do | |||
%{} | |||
end | |||
# If we only get the naked data, wrap in an object | |||
def to_map(%{} = data, opts) do | |||
to_map(%Object{data: data}, opts) | |||
end | |||
end |
@@ -3,133 +3,14 @@ | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.TwitterAPI.TwitterAPI do | |||
alias Pleroma.Activity | |||
alias Pleroma.Emails.Mailer | |||
alias Pleroma.Emails.UserEmail | |||
alias Pleroma.Repo | |||
alias Pleroma.User | |||
alias Pleroma.UserInviteToken | |||
alias Pleroma.Web.ActivityPub.ActivityPub | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.TwitterAPI.UserView | |||
import Ecto.Query | |||
require Pleroma.Constants | |||
def create_status(%User{} = user, %{"status" => _} = data) do | |||
CommonAPI.post(user, data) | |||
end | |||
def delete(%User{} = user, id) do | |||
with %Activity{data: %{"type" => _type}} <- Activity.get_by_id(id), | |||
{:ok, activity} <- CommonAPI.delete(id, user) do | |||
{:ok, activity} | |||
end | |||
end | |||
def follow(%User{} = follower, params) do | |||
with {:ok, %User{} = followed} <- get_user(params) do | |||
CommonAPI.follow(follower, followed) | |||
end | |||
end | |||
def unfollow(%User{} = follower, params) do | |||
with {:ok, %User{} = unfollowed} <- get_user(params), | |||
{:ok, follower} <- CommonAPI.unfollow(follower, unfollowed) do | |||
{:ok, follower, unfollowed} | |||
end | |||
end | |||
def block(%User{} = blocker, params) do | |||
with {:ok, %User{} = blocked} <- get_user(params), | |||
{:ok, blocker} <- User.block(blocker, blocked), | |||
{:ok, _activity} <- ActivityPub.block(blocker, blocked) do | |||
{:ok, blocker, blocked} | |||
else | |||
err -> err | |||
end | |||
end | |||
def unblock(%User{} = blocker, params) do | |||
with {:ok, %User{} = blocked} <- get_user(params), | |||
{:ok, blocker} <- User.unblock(blocker, blocked), | |||
{:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do | |||
{:ok, blocker, blocked} | |||
else | |||
err -> err | |||
end | |||
end | |||
def repeat(%User{} = user, ap_id_or_id) do | |||
with {:ok, _announce, %{data: %{"id" => id}}} <- CommonAPI.repeat(ap_id_or_id, user), | |||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do | |||
{:ok, activity} | |||
end | |||
end | |||
def unrepeat(%User{} = user, ap_id_or_id) do | |||
with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user), | |||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do | |||
{:ok, activity} | |||
end | |||
end | |||
def pin(%User{} = user, ap_id_or_id) do | |||
CommonAPI.pin(ap_id_or_id, user) | |||
end | |||
def unpin(%User{} = user, ap_id_or_id) do | |||
CommonAPI.unpin(ap_id_or_id, user) | |||
end | |||
def fav(%User{} = user, ap_id_or_id) do | |||
with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user), | |||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do | |||
{:ok, activity} | |||
end | |||
end | |||
def unfav(%User{} = user, ap_id_or_id) do | |||
with {:ok, _unfav, _fav, %{data: %{"id" => id}}} <- CommonAPI.unfavorite(ap_id_or_id, user), | |||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do | |||
{:ok, activity} | |||
end | |||
end | |||
def upload(%Plug.Upload{} = file, %User{} = user, format \\ "xml") do | |||
{:ok, object} = ActivityPub.upload(file, actor: User.ap_id(user)) | |||
url = List.first(object.data["url"]) | |||
href = url["href"] | |||
type = url["mediaType"] | |||
case format do | |||
"xml" -> | |||
# Fake this as good as possible... | |||
""" | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<rsp stat="ok" xmlns:atom="http://www.w3.org/2005/Atom"> | |||
<mediaid>#{object.id}</mediaid> | |||
<media_id>#{object.id}</media_id> | |||
<media_id_string>#{object.id}</media_id_string> | |||
<media_url>#{href}</media_url> | |||
<mediaurl>#{href}</mediaurl> | |||
<atom:link rel="enclosure" href="#{href}" type="#{type}"></atom:link> | |||
</rsp> | |||
""" | |||
"json" -> | |||
%{ | |||
media_id: object.id, | |||
media_id_string: "#{object.id}}", | |||
media_url: href, | |||
size: 0 | |||
} | |||
|> Jason.encode!() | |||
end | |||
end | |||
def register_user(params, opts \\ []) do | |||
token = params["token"] | |||
@@ -236,80 +117,4 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do | |||
{:error, "unknown user"} | |||
end | |||
end | |||
def get_user(user \\ nil, params) do | |||
case params do | |||
%{"user_id" => user_id} -> | |||
case User.get_cached_by_nickname_or_id(user_id) do | |||
nil -> | |||
{:error, "No user with such user_id"} | |||
%User{info: %{deactivated: true}} -> | |||
{:error, "User has been disabled"} | |||
user -> | |||
{:ok, user} | |||
end | |||
%{"screen_name" => nickname} -> | |||
case User.get_cached_by_nickname(nickname) do | |||
nil -> {:error, "No user with such screen_name"} | |||
target -> {:ok, target} | |||
end | |||
_ -> | |||
if user do | |||
{:ok, user} | |||
else | |||
{:error, "You need to specify screen_name or user_id"} | |||
end | |||
end | |||
end | |||
defp parse_int(string, default) | |||
defp parse_int(string, default) when is_binary(string) do | |||
with {n, _} <- Integer.parse(string) do | |||
n | |||
else | |||
_e -> default | |||
end | |||
end | |||
defp parse_int(_, default), do: default | |||
# TODO: unify the search query with MastoAPI one and do only pagination here | |||
def search(_user, %{"q" => query} = params) do | |||
limit = parse_int(params["rpp"], 20) | |||
page = parse_int(params["page"], 1) | |||
offset = (page - 1) * limit | |||
q = | |||
from( | |||
[a, o] in Activity.with_preloaded_object(Activity), | |||
where: fragment("?->>'type' = 'Create'", a.data), | |||
where: ^Pleroma.Constants.as_public() in a.recipients, | |||
where: | |||
fragment( | |||
"to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)", | |||
o.data, | |||
^query | |||
), | |||
limit: ^limit, | |||
offset: ^offset, | |||
# this one isn't indexed so psql won't take the wrong index. | |||
order_by: [desc: :inserted_at] | |||
) | |||
_activities = Repo.all(q) | |||
end | |||
def get_external_profile(for_user, uri) do | |||
with {:ok, %User{} = user} <- User.get_or_fetch(uri) do | |||
{:ok, UserView.render("show.json", %{user: user, for: for_user})} | |||
else | |||
_e -> | |||
{:error, "Couldn't find user"} | |||
end | |||
end | |||
end |
@@ -5,448 +5,16 @@ | |||
defmodule Pleroma.Web.TwitterAPI.Controller do | |||
use Pleroma.Web, :controller | |||
import Pleroma.Web.ControllerHelper, only: [json_response: 3] | |||
alias Ecto.Changeset | |||
alias Pleroma.Activity | |||
alias Pleroma.Formatter | |||
alias Pleroma.Notification | |||
alias Pleroma.Object | |||
alias Pleroma.Repo | |||
alias Pleroma.User | |||
alias Pleroma.Web.ActivityPub.ActivityPub | |||
alias Pleroma.Web.ActivityPub.Visibility | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.CommonAPI.Utils | |||
alias Pleroma.Web.OAuth.Token | |||
alias Pleroma.Web.TwitterAPI.ActivityView | |||
alias Pleroma.Web.TwitterAPI.NotificationView | |||
alias Pleroma.Web.TwitterAPI.TokenView | |||
alias Pleroma.Web.TwitterAPI.TwitterAPI | |||
alias Pleroma.Web.TwitterAPI.UserView | |||
require Logger | |||
plug(Pleroma.Plugs.RateLimiter, :password_reset when action == :password_reset) | |||
plug(:only_if_public_instance when action in [:public_timeline, :public_and_external_timeline]) | |||
action_fallback(:errors) | |||
def verify_credentials(%{assigns: %{user: user}} = conn, _params) do | |||
token = Phoenix.Token.sign(conn, "user socket", user.id) | |||
conn | |||
|> put_view(UserView) | |||
|> render("show.json", %{user: user, token: token, for: user}) | |||
end | |||
def status_update(%{assigns: %{user: user}} = conn, %{"status" => _} = status_data) do | |||
with media_ids <- extract_media_ids(status_data), | |||
{:ok, activity} <- | |||
TwitterAPI.create_status(user, Map.put(status_data, "media_ids", media_ids)) do | |||
conn | |||
|> json(ActivityView.render("activity.json", activity: activity, for: user)) | |||
else | |||
_ -> empty_status_reply(conn) | |||
end | |||
end | |||
def status_update(conn, _status_data) do | |||
empty_status_reply(conn) | |||
end | |||
defp empty_status_reply(conn) do | |||
bad_request_reply(conn, "Client must provide a 'status' parameter with a value.") | |||
end | |||
defp extract_media_ids(status_data) do | |||
with media_ids when not is_nil(media_ids) <- status_data["media_ids"], | |||
split_ids <- String.split(media_ids, ","), | |||
clean_ids <- Enum.reject(split_ids, fn id -> String.length(id) == 0 end) do | |||
clean_ids | |||
else | |||
_e -> [] | |||
end | |||
end | |||
def public_and_external_timeline(%{assigns: %{user: user}} = conn, params) do | |||
params = | |||
params | |||
|> Map.put("type", ["Create", "Announce"]) | |||
|> Map.put("blocking_user", user) | |||
activities = ActivityPub.fetch_public_activities(params) | |||
conn | |||
|> put_view(ActivityView) | |||
|> render("index.json", %{activities: activities, for: user}) | |||
end | |||
def public_timeline(%{assigns: %{user: user}} = conn, params) do | |||
params = | |||
params | |||
|> Map.put("type", ["Create", "Announce"]) | |||
|> Map.put("local_only", true) | |||
|> Map.put("blocking_user", user) | |||
activities = ActivityPub.fetch_public_activities(params) | |||
conn | |||
|> put_view(ActivityView) | |||
|> render("index.json", %{activities: activities, for: user}) | |||
end | |||
def friends_timeline(%{assigns: %{user: user}} = conn, params) do | |||
params = | |||
params | |||
|> Map.put("type", ["Create", "Announce", "Follow", "Like"]) | |||
|> Map.put("blocking_user", user) | |||
|> Map.put("user", user) | |||
activities = ActivityPub.fetch_activities([user.ap_id | user.following], params) | |||
conn | |||
|> put_view(ActivityView) | |||
|> render("index.json", %{activities: activities, for: user}) | |||
end | |||
def show_user(conn, params) do | |||
for_user = conn.assigns.user | |||
with {:ok, shown} <- TwitterAPI.get_user(params), | |||
true <- | |||
User.auth_active?(shown) || | |||
(for_user && (for_user.id == shown.id || User.superuser?(for_user))) do | |||
params = | |||
if for_user do | |||
%{user: shown, for: for_user} | |||
else | |||
%{user: shown} | |||
end | |||
conn | |||
|> put_view(UserView) | |||
|> render("show.json", params) | |||
else | |||
{:error, msg} -> | |||
bad_request_reply(conn, msg) | |||
false -> | |||
conn | |||
|> put_status(404) | |||
|> json(%{error: "Unconfirmed user"}) | |||
end | |||
end | |||
def user_timeline(%{assigns: %{user: user}} = conn, params) do | |||
case TwitterAPI.get_user(user, params) do | |||
{:ok, target_user} -> | |||
# Twitter and ActivityPub use a different name and sense for this parameter. | |||
{include_rts, params} = Map.pop(params, "include_rts") | |||
params = | |||
case include_rts do | |||
x when x == "false" or x == "0" -> Map.put(params, "exclude_reblogs", "true") | |||
_ -> params | |||
end | |||
activities = ActivityPub.fetch_user_activities(target_user, user, params) | |||
conn | |||
|> put_view(ActivityView) | |||
|> render("index.json", %{activities: activities, for: user}) | |||
{:error, msg} -> | |||
bad_request_reply(conn, msg) | |||
end | |||
end | |||
def mentions_timeline(%{assigns: %{user: user}} = conn, params) do | |||
params = | |||
params | |||
|> Map.put("type", ["Create", "Announce", "Follow", "Like"]) | |||
|> Map.put("blocking_user", user) | |||
|> Map.put(:visibility, ~w[unlisted public private]) | |||
activities = ActivityPub.fetch_activities([user.ap_id], params) | |||
conn | |||
|> put_view(ActivityView) | |||
|> render("index.json", %{activities: activities, for: user}) | |||
end | |||
def dm_timeline(%{assigns: %{user: user}} = conn, params) do | |||
params = | |||
params | |||
|> Map.put("type", "Create") | |||
|> Map.put("blocking_user", user) | |||
|> Map.put("user", user) | |||
|> Map.put(:visibility, "direct") | |||
|> Map.put(:order, :desc) | |||
activities = | |||
ActivityPub.fetch_activities_query([user.ap_id], params) | |||
|> Repo.all() | |||
conn | |||
|> put_view(ActivityView) | |||
|> render("index.json", %{activities: activities, for: user}) | |||
end | |||
def notifications(%{assigns: %{user: user}} = conn, params) do | |||
params = | |||
if Map.has_key?(params, "with_muted") do | |||
Map.put(params, :with_muted, params["with_muted"] in [true, "True", "true", "1"]) | |||
else | |||
params | |||
end | |||
notifications = Notification.for_user(user, params) | |||
conn | |||
|> put_view(NotificationView) | |||
|> render("notification.json", %{notifications: notifications, for: user}) | |||
end | |||
def notifications_read(%{assigns: %{user: user}} = conn, %{"latest_id" => latest_id} = params) do | |||
Notification.set_read_up_to(user, latest_id) | |||
notifications = Notification.for_user(user, params) | |||
conn | |||
|> put_view(NotificationView) | |||
|> render("notification.json", %{notifications: notifications, for: user}) | |||
end | |||
def notifications_read(%{assigns: %{user: _user}} = conn, _) do | |||
bad_request_reply(conn, "You need to specify latest_id") | |||
end | |||
def follow(%{assigns: %{user: user}} = conn, params) do | |||
case TwitterAPI.follow(user, params) do | |||
{:ok, user, followed, _activity} -> | |||
conn | |||
|> put_view(UserView) | |||
|> render("show.json", %{user: followed, for: user}) | |||
{:error, msg} -> | |||
forbidden_json_reply(conn, msg) | |||
end | |||
end | |||
def block(%{assigns: %{user: user}} = conn, params) do | |||
case TwitterAPI.block(user, params) do | |||
{:ok, user, blocked} -> | |||
conn | |||
|> put_view(UserView) | |||
|> render("show.json", %{user: blocked, for: user}) | |||
{:error, msg} -> | |||
forbidden_json_reply(conn, msg) | |||
end | |||
end | |||
def unblock(%{assigns: %{user: user}} = conn, params) do | |||
case TwitterAPI.unblock(user, params) do | |||
{:ok, user, blocked} -> | |||
conn | |||
|> put_view(UserView) | |||
|> render("show.json", %{user: blocked, for: user}) | |||
{:error, msg} -> | |||
forbidden_json_reply(conn, msg) | |||
end | |||
end | |||
def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
with {:ok, activity} <- TwitterAPI.delete(user, id) do | |||
conn | |||
|> put_view(ActivityView) | |||
|> render("activity.json", %{activity: activity, for: user}) | |||
end | |||
end | |||
def unfollow(%{assigns: %{user: user}} = conn, params) do | |||
case TwitterAPI.unfollow(user, params) do | |||
{:ok, user, unfollowed} -> | |||
conn | |||
|> put_view(UserView) | |||
|> render("show.json", %{user: unfollowed, for: user}) | |||
{:error, msg} -> | |||
forbidden_json_reply(conn, msg) | |||
end | |||
end | |||
def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
with %Activity{} = activity <- Activity.get_by_id(id), | |||
true <- Visibility.visible_for_user?(activity, user) do | |||
conn | |||
|> put_view(ActivityView) | |||
|> render("activity.json", %{activity: activity, for: user}) | |||
end | |||
end | |||
def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
with context when is_binary(context) <- Utils.conversation_id_to_context(id), | |||
activities <- | |||
ActivityPub.fetch_activities_for_context(context, %{ | |||
"blocking_user" => user, | |||
"user" => user | |||
}) do | |||
conn | |||
|> put_view(ActivityView) | |||
|> render("index.json", %{activities: activities, for: user}) | |||
end | |||
end | |||
@doc """ | |||
Updates metadata of uploaded media object. | |||
Derived from [Twitter API endpoint](https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-metadata-create). | |||
""" | |||
def update_media(%{assigns: %{user: user}} = conn, %{"media_id" => id} = data) do | |||
object = Repo.get(Object, id) | |||
description = get_in(data, ["alt_text", "text"]) || data["name"] || data["description"] | |||
{conn, status, response_body} = | |||
cond do | |||
!object -> | |||
{halt(conn), :not_found, ""} | |||
!Object.authorize_mutation(object, user) -> | |||
{halt(conn), :forbidden, "You can only update your own uploads."} | |||
!is_binary(description) -> | |||
{conn, :not_modified, ""} | |||
true -> | |||
new_data = Map.put(object.data, "name", description) | |||
{:ok, _} = | |||
object | |||
|> Object.change(%{data: new_data}) | |||
|> Repo.update() | |||
{conn, :no_content, ""} | |||
end | |||
conn | |||
|> put_status(status) | |||
|> json(response_body) | |||
end | |||
def upload(%{assigns: %{user: user}} = conn, %{"media" => media}) do | |||
response = TwitterAPI.upload(media, user) | |||
conn | |||
|> put_resp_content_type("application/atom+xml") | |||
|> send_resp(200, response) | |||
end | |||
def upload_json(%{assigns: %{user: user}} = conn, %{"media" => media}) do | |||
response = TwitterAPI.upload(media, user, "json") | |||
conn | |||
|> json_reply(200, response) | |||
end | |||
def get_by_id_or_ap_id(id) do | |||
activity = Activity.get_by_id(id) || Activity.get_create_by_object_ap_id(id) | |||
if activity.data["type"] == "Create" do | |||
activity | |||
else | |||
Activity.get_create_by_object_ap_id(activity.data["object"]) | |||
end | |||
end | |||
def favorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
with {:ok, activity} <- TwitterAPI.fav(user, id) do | |||
conn | |||
|> put_view(ActivityView) | |||
|> render("activity.json", %{activity: activity, for: user}) | |||
else | |||
_ -> json_reply(conn, 400, Jason.encode!(%{})) | |||
end | |||
end | |||
def unfavorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
with {:ok, activity} <- TwitterAPI.unfav(user, id) do | |||
conn | |||
|> put_view(ActivityView) | |||
|> render("activity.json", %{activity: activity, for: user}) | |||
else | |||
_ -> json_reply(conn, 400, Jason.encode!(%{})) | |||
end | |||
end | |||
def retweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
with {:ok, activity} <- TwitterAPI.repeat(user, id) do | |||
conn | |||
|> put_view(ActivityView) | |||
|> render("activity.json", %{activity: activity, for: user}) | |||
else | |||
_ -> json_reply(conn, 400, Jason.encode!(%{})) | |||
end | |||
end | |||
def unretweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
with {:ok, activity} <- TwitterAPI.unrepeat(user, id) do | |||
conn | |||
|> put_view(ActivityView) | |||
|> render("activity.json", %{activity: activity, for: user}) | |||
else | |||
_ -> json_reply(conn, 400, Jason.encode!(%{})) | |||
end | |||
end | |||
def pin(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
with {:ok, activity} <- TwitterAPI.pin(user, id) do | |||
conn | |||
|> put_view(ActivityView) | |||
|> render("activity.json", %{activity: activity, for: user}) | |||
else | |||
{:error, message} -> bad_request_reply(conn, message) | |||
err -> err | |||
end | |||
end | |||
def unpin(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
with {:ok, activity} <- TwitterAPI.unpin(user, id) do | |||
conn | |||
|> put_view(ActivityView) | |||
|> render("activity.json", %{activity: activity, for: user}) | |||
else | |||
{:error, message} -> bad_request_reply(conn, message) | |||
err -> err | |||
end | |||
end | |||
def register(conn, params) do | |||
with {:ok, user} <- TwitterAPI.register_user(params) do | |||
conn | |||
|> put_view(UserView) | |||
|> render("show.json", %{user: user}) | |||
else | |||
{:error, errors} -> | |||
conn | |||
|> json_reply(400, Jason.encode!(errors)) | |||
end | |||
end | |||
def password_reset(conn, params) do | |||
nickname_or_email = params["email"] || params["nickname"] | |||
with {:ok, _} <- TwitterAPI.password_reset(nickname_or_email) do | |||
json_response(conn, :no_content, "") | |||
else | |||
{:error, "unknown user"} -> | |||
send_resp(conn, :not_found, "") | |||
{:error, _} -> | |||
send_resp(conn, :bad_request, "") | |||
end | |||
end | |||
def confirm_email(conn, %{"user_id" => uid, "token" => token}) do | |||
with %User{} = user <- User.get_cached_by_id(uid), | |||
true <- user.local, | |||
@@ -460,147 +28,6 @@ defmodule Pleroma.Web.TwitterAPI.Controller do | |||
end | |||
end | |||
def resend_confirmation_email(conn, params) do | |||
nickname_or_email = params["email"] || params["nickname"] | |||
with %User{} = user <- User.get_by_nickname_or_email(nickname_or_email), | |||
{:ok, _} <- User.try_send_confirmation_email(user) do | |||
conn | |||
|> json_response(:no_content, "") | |||
end | |||
end | |||
def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do | |||
change = Changeset.change(user, %{avatar: nil}) | |||
{:ok, user} = User.update_and_set_cache(change) | |||
CommonAPI.update(user) | |||
conn | |||
|> put_view(UserView) | |||
|> render("show.json", %{user: user, for: user}) | |||
end | |||
def update_avatar(%{assigns: %{user: user}} = conn, params) do | |||
{:ok, object} = ActivityPub.upload(params, type: :avatar) | |||
change = Changeset.change(user, %{avatar: object.data}) | |||
{:ok, user} = User.update_and_set_cache(change) | |||
CommonAPI.update(user) | |||
conn | |||
|> put_view(UserView) | |||
|> render("show.json", %{user: user, for: user}) | |||
end | |||
def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do | |||
with new_info <- %{"banner" => %{}}, | |||
info_cng <- User.Info.profile_update(user.info, new_info), | |||
changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), | |||
{:ok, user} <- User.update_and_set_cache(changeset) do | |||
CommonAPI.update(user) | |||
response = %{url: nil} |> Jason.encode!() | |||
conn | |||
|> json_reply(200, response) | |||
end | |||
end | |||
def update_banner(%{assigns: %{user: user}} = conn, params) do | |||
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner), | |||
new_info <- %{"banner" => object.data}, | |||
info_cng <- User.Info.profile_update(user.info, new_info), | |||
changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), | |||
{:ok, user} <- User.update_and_set_cache(changeset) do | |||
CommonAPI.update(user) | |||
%{"url" => [%{"href" => href} | _]} = object.data | |||
response = %{url: href} |> Jason.encode!() | |||
conn | |||
|> json_reply(200, response) | |||
end | |||
end | |||
def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do | |||
with new_info <- %{"background" => %{}}, | |||
info_cng <- User.Info.profile_update(user.info, new_info), | |||
changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), | |||
{:ok, _user} <- User.update_and_set_cache(changeset) do | |||
response = %{url: nil} |> Jason.encode!() | |||
conn | |||
|> json_reply(200, response) | |||
end | |||
end | |||
def update_background(%{assigns: %{user: user}} = conn, params) do | |||
with {:ok, object} <- ActivityPub.upload(params, type: :background), | |||
new_info <- %{"background" => object.data}, | |||
info_cng <- User.Info.profile_update(user.info, new_info), | |||
changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), | |||
{:ok, _user} <- User.update_and_set_cache(changeset) do | |||
%{"url" => [%{"href" => href} | _]} = object.data | |||
response = %{url: href} |> Jason.encode!() | |||
conn | |||
|> json_reply(200, response) | |||
end | |||
end | |||
def external_profile(%{assigns: %{user: current_user}} = conn, %{"profileurl" => uri}) do | |||
with {:ok, user_map} <- TwitterAPI.get_external_profile(current_user, uri), | |||
response <- Jason.encode!(user_map) do | |||
conn | |||
|> json_reply(200, response) | |||
else | |||
_e -> | |||
conn | |||
|> put_status(404) | |||
|> json(%{error: "Can't find user"}) | |||
end | |||
end | |||
def followers(%{assigns: %{user: for_user}} = conn, params) do | |||
{:ok, page} = Ecto.Type.cast(:integer, params["page"] || 1) | |||
with {:ok, user} <- TwitterAPI.get_user(for_user, params), | |||
{:ok, followers} <- User.get_followers(user, page) do | |||
followers = | |||
cond do | |||
for_user && user.id == for_user.id -> followers | |||
user.info.hide_followers -> [] | |||
true -> followers | |||
end | |||
conn | |||
|> put_view(UserView) | |||
|> render("index.json", %{users: followers, for: conn.assigns[:user]}) | |||
else | |||
_e -> bad_request_reply(conn, "Can't get followers") | |||
end | |||
end | |||
def friends(%{assigns: %{user: for_user}} = conn, params) do | |||
{:ok, page} = Ecto.Type.cast(:integer, params["page"] || 1) | |||
{:ok, export} = Ecto.Type.cast(:boolean, params["all"] || false) | |||
page = if export, do: nil, else: page | |||
with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params), | |||
{:ok, friends} <- User.get_friends(user, page) do | |||
friends = | |||
cond do | |||
for_user && user.id == for_user.id -> friends | |||
user.info.hide_follows -> [] | |||
true -> friends | |||
end | |||
conn | |||
|> put_view(UserView) | |||
|> render("index.json", %{users: friends, for: conn.assigns[:user]}) | |||
else | |||
_e -> bad_request_reply(conn, "Can't get friends") | |||
end | |||
end | |||
def oauth_tokens(%{assigns: %{user: user}} = conn, _params) do | |||
with oauth_tokens <- Token.get_user_tokens(user) do | |||
conn | |||
@@ -615,160 +42,16 @@ defmodule Pleroma.Web.TwitterAPI.Controller do | |||
json_reply(conn, 201, "") | |||
end | |||
def blocks(%{assigns: %{user: user}} = conn, _params) do | |||
with blocked_users <- User.blocked_users(user) do | |||
conn | |||
|> put_view(UserView) | |||
|> render("index.json", %{users: blocked_users, for: user}) | |||
end | |||
end | |||
def friend_requests(conn, params) do | |||
with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params), | |||
{:ok, friend_requests} <- User.get_follow_requests(user) do | |||
conn | |||
|> put_view(UserView) | |||
|> render("index.json", %{users: friend_requests, for: conn.assigns[:user]}) | |||
else | |||
_e -> bad_request_reply(conn, "Can't get friend requests") | |||
end | |||
end | |||
def approve_friend_request(conn, %{"user_id" => uid} = _params) do | |||
with followed <- conn.assigns[:user], | |||
%User{} = follower <- User.get_cached_by_id(uid), | |||
{:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do | |||
conn | |||
|> put_view(UserView) | |||
|> render("show.json", %{user: follower, for: followed}) | |||
else | |||
e -> bad_request_reply(conn, "Can't approve user: #{inspect(e)}") | |||
end | |||
end | |||
def deny_friend_request(conn, %{"user_id" => uid} = _params) do | |||
with followed <- conn.assigns[:user], | |||
%User{} = follower <- User.get_cached_by_id(uid), | |||
{:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do | |||
conn | |||
|> put_view(UserView) | |||
|> render("show.json", %{user: follower, for: followed}) | |||
else | |||
e -> bad_request_reply(conn, "Can't deny user: #{inspect(e)}") | |||
end | |||
end | |||
def friends_ids(%{assigns: %{user: user}} = conn, _params) do | |||
with {:ok, friends} <- User.get_friends(user) do | |||
ids = | |||
friends | |||
|> Enum.map(fn x -> x.id end) | |||
|> Jason.encode!() | |||
json(conn, ids) | |||
else | |||
_e -> bad_request_reply(conn, "Can't get friends") | |||
end | |||
end | |||
def empty_array(conn, _params) do | |||
json(conn, Jason.encode!([])) | |||
end | |||
def raw_empty_array(conn, _params) do | |||
json(conn, []) | |||
end | |||
defp build_info_cng(user, params) do | |||
info_params = | |||
[ | |||
"no_rich_text", | |||
"locked", | |||
"hide_followers", | |||
"hide_follows", | |||
"hide_favorites", | |||
"show_role", | |||
"skip_thread_containment" | |||
] | |||
|> Enum.reduce(%{}, fn key, res -> | |||
if value = params[key] do | |||
Map.put(res, key, value == "true") | |||
else | |||
res | |||
end | |||
end) | |||
info_params = | |||
if value = params["default_scope"] do | |||
Map.put(info_params, "default_scope", value) | |||
else | |||
info_params | |||
end | |||
User.Info.profile_update(user.info, info_params) | |||
end | |||
defp parse_profile_bio(user, params) do | |||
if bio = params["description"] do | |||
emojis_text = (params["description"] || "") <> " " <> (params["name"] || "") | |||
emojis = | |||
((user.info.emoji || []) ++ Formatter.get_emoji_map(emojis_text)) | |||
|> Enum.dedup() | |||
user_info = | |||
user.info | |||
|> Map.put( | |||
"emoji", | |||
emojis | |||
) | |||
params | |||
|> Map.put("bio", User.parse_bio(bio, user)) | |||
|> Map.put("info", user_info) | |||
else | |||
params | |||
end | |||
end | |||
def update_profile(%{assigns: %{user: user}} = conn, params) do | |||
params = parse_profile_bio(user, params) | |||
info_cng = build_info_cng(user, params) | |||
with changeset <- User.update_changeset(user, params), | |||
changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng), | |||
{:ok, user} <- User.update_and_set_cache(changeset) do | |||
CommonAPI.update(user) | |||
conn | |||
|> put_view(UserView) | |||
|> render("user.json", %{user: user, for: user}) | |||
else | |||
error -> | |||
Logger.debug("Can't update user: #{inspect(error)}") | |||
bad_request_reply(conn, "Can't update user") | |||
end | |||
end | |||
def search(%{assigns: %{user: user}} = conn, %{"q" => _query} = params) do | |||
activities = TwitterAPI.search(user, params) | |||
def errors(conn, {:param_cast, _}) do | |||
conn | |||
|> put_view(ActivityView) | |||
|> render("index.json", %{activities: activities, for: user}) | |||
|> put_status(400) | |||
|> json("Invalid parameters") | |||
end | |||
def search_user(%{assigns: %{user: user}} = conn, %{"query" => query}) do | |||
users = User.search(query, resolve: true, for_user: user) | |||
def errors(conn, _) do | |||
conn | |||
|> put_view(UserView) | |||
|> render("index.json", %{users: users, for: user}) | |||
end | |||
defp bad_request_reply(conn, error_message) do | |||
json = error_json(conn, error_message) | |||
json_reply(conn, 400, json) | |||
|> put_status(500) | |||
|> json("Something went wrong") | |||
end | |||
defp json_reply(conn, status, json) do | |||
@@ -777,36 +60,27 @@ defmodule Pleroma.Web.TwitterAPI.Controller do | |||
|> send_resp(status, json) | |||
end | |||
defp forbidden_json_reply(conn, error_message) do | |||
json = error_json(conn, error_message) | |||
json_reply(conn, 403, json) | |||
end | |||
def notifications_read(%{assigns: %{user: user}} = conn, %{"latest_id" => latest_id} = params) do | |||
Notification.set_read_up_to(user, latest_id) | |||
def only_if_public_instance(%{assigns: %{user: %User{}}} = conn, _), do: conn | |||
notifications = Notification.for_user(user, params) | |||
def only_if_public_instance(conn, _) do | |||
if Pleroma.Config.get([:instance, :public]) do | |||
conn | |||
else | |||
conn | |||
|> forbidden_json_reply("Invalid credentials.") | |||
|> halt() | |||
end | |||
conn | |||
# XXX: This is a hack because pleroma-fe still uses that API. | |||
|> put_view(Pleroma.Web.MastodonAPI.NotificationView) | |||
|> render("index.json", %{notifications: notifications, for: user}) | |||
end | |||
defp error_json(conn, error_message) do | |||
%{"error" => error_message, "request" => conn.request_path} |> Jason.encode!() | |||
def notifications_read(%{assigns: %{user: _user}} = conn, _) do | |||
bad_request_reply(conn, "You need to specify latest_id") | |||
end | |||
def errors(conn, {:param_cast, _}) do | |||
conn | |||
|> put_status(400) | |||
|> json("Invalid parameters") | |||
defp bad_request_reply(conn, error_message) do | |||
json = error_json(conn, error_message) | |||
json_reply(conn, 400, json) | |||
end | |||
def errors(conn, _) do | |||
conn | |||
|> put_status(500) | |||
|> json("Something went wrong") | |||
defp error_json(conn, error_message) do | |||
%{"error" => error_message, "request" => conn.request_path} |> Jason.encode!() | |||
end | |||
end |
@@ -1,366 +0,0 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.TwitterAPI.ActivityView do | |||
use Pleroma.Web, :view | |||
alias Pleroma.Activity | |||
alias Pleroma.Formatter | |||
alias Pleroma.HTML | |||
alias Pleroma.Object | |||
alias Pleroma.Repo | |||
alias Pleroma.User | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.CommonAPI.Utils | |||
alias Pleroma.Web.MastodonAPI.StatusView | |||
alias Pleroma.Web.TwitterAPI.ActivityView | |||
alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter | |||
alias Pleroma.Web.TwitterAPI.UserView | |||
import Ecto.Query | |||
require Logger | |||
require Pleroma.Constants | |||
defp query_context_ids([]), do: [] | |||
defp query_context_ids(contexts) do | |||
query = from(o in Object, where: fragment("(?)->>'id' = ANY(?)", o.data, ^contexts)) | |||
Repo.all(query) | |||
end | |||
defp query_users([]), do: [] | |||
defp query_users(user_ids) do | |||
query = from(user in User, where: user.ap_id in ^user_ids) | |||
Repo.all(query) | |||
end | |||
defp collect_context_ids(activities) do | |||
_contexts = | |||
activities | |||
|> Enum.reject(& &1.data["context_id"]) | |||
|> Enum.map(fn %{data: data} -> | |||
data["context"] | |||
end) | |||
|> Enum.filter(& &1) | |||
|> query_context_ids() | |||
|> Enum.reduce(%{}, fn %{data: %{"id" => ap_id}, id: id}, acc -> | |||
Map.put(acc, ap_id, id) | |||
end) | |||
end | |||
defp collect_users(activities) do | |||
activities | |||
|> Enum.map(fn activity -> | |||
case activity.data do | |||
data = %{"type" => "Follow"} -> | |||
[data["actor"], data["object"]] | |||
data -> | |||
[data["actor"]] | |||
end ++ activity.recipients | |||
end) | |||
|> List.flatten() | |||
|> Enum.uniq() | |||
|> query_users() | |||
|> Enum.reduce(%{}, fn user, acc -> | |||
Map.put(acc, user.ap_id, user) | |||
end) | |||
end | |||
defp get_context_id(%{data: %{"context_id" => context_id}}, _) when not is_nil(context_id), | |||
do: context_id | |||
defp get_context_id(%{data: %{"context" => nil}}, _), do: nil | |||
defp get_context_id(%{data: %{"context" => context}}, options) do | |||
cond do | |||
id = options[:context_ids][context] -> id | |||
true -> Utils.context_to_conversation_id(context) | |||
end | |||
end | |||
defp get_context_id(_, _), do: nil | |||
defp get_user(ap_id, opts) do | |||
cond do | |||
user = opts[:users][ap_id] -> | |||
user | |||
String.ends_with?(ap_id, "/followers") -> | |||
nil | |||
ap_id == Pleroma.Constants.as_public() -> | |||
nil | |||
user = User.get_cached_by_ap_id(ap_id) -> | |||
user | |||
user = User.get_by_guessed_nickname(ap_id) -> | |||
user | |||
true -> | |||
User.error_user(ap_id) | |||
end | |||
end | |||
def render("index.json", opts) do | |||
context_ids = collect_context_ids(opts.activities) | |||
users = collect_users(opts.activities) | |||
opts = | |||
opts | |||
|> Map.put(:context_ids, context_ids) | |||
|> Map.put(:users, users) | |||
safe_render_many( | |||
opts.activities, | |||
ActivityView, | |||
"activity.json", | |||
opts | |||
) | |||
end | |||
def render("activity.json", %{activity: %{data: %{"type" => "Delete"}} = activity} = opts) do | |||
user = get_user(activity.data["actor"], opts) | |||
created_at = activity.data["published"] |> Utils.date_to_asctime() | |||
%{ | |||
"id" => activity.id, | |||
"uri" => activity.data["object"], | |||
"user" => UserView.render("show.json", %{user: user, for: opts[:for]}), | |||
"attentions" => [], | |||
"statusnet_html" => "deleted notice {{tag", | |||
"text" => "deleted notice {{tag", | |||
"is_local" => activity.local, | |||
"is_post_verb" => false, | |||
"created_at" => created_at, | |||
"in_reply_to_status_id" => nil, | |||
"external_url" => activity.data["id"], | |||
"activity_type" => "delete" | |||
} | |||
end | |||
def render("activity.json", %{activity: %{data: %{"type" => "Follow"}} = activity} = opts) do | |||
user = get_user(activity.data["actor"], opts) | |||
created_at = activity.data["published"] || DateTime.to_iso8601(activity.inserted_at) | |||
created_at = created_at |> Utils.date_to_asctime() | |||
followed = get_user(activity.data["object"], opts) | |||
text = "#{user.nickname} started following #{followed.nickname}" | |||
%{ | |||
"id" => activity.id, | |||
"user" => UserView.render("show.json", %{user: user, for: opts[:for]}), | |||
"attentions" => [], | |||
"statusnet_html" => text, | |||
"text" => text, | |||
"is_local" => activity.local, | |||
"is_post_verb" => false, | |||
"created_at" => created_at, | |||
"in_reply_to_status_id" => nil, | |||
"external_url" => activity.data["id"], | |||
"activity_type" => "follow" | |||
} | |||
end | |||
def render("activity.json", %{activity: %{data: %{"type" => "Announce"}} = activity} = opts) do | |||
user = get_user(activity.data["actor"], opts) | |||
created_at = activity.data["published"] |> Utils.date_to_asctime() | |||
announced_activity = Activity.get_create_by_object_ap_id(activity.data["object"]) | |||
text = "#{user.nickname} repeated a status." | |||
retweeted_status = render("activity.json", Map.merge(opts, %{activity: announced_activity})) | |||
%{ | |||
"id" => activity.id, | |||
"user" => UserView.render("show.json", %{user: user, for: opts[:for]}), | |||
"statusnet_html" => text, | |||
"text" => text, | |||
"is_local" => activity.local, | |||
"is_post_verb" => false, | |||
"uri" => "tag:#{activity.data["id"]}:objectType=note", | |||
"created_at" => created_at, | |||
"retweeted_status" => retweeted_status, | |||
"statusnet_conversation_id" => get_context_id(announced_activity, opts), | |||
"external_url" => activity.data["id"], | |||
"activity_type" => "repeat" | |||
} | |||
end | |||
def render("activity.json", %{activity: %{data: %{"type" => "Like"}} = activity} = opts) do | |||
user = get_user(activity.data["actor"], opts) | |||
liked_activity = Activity.get_create_by_object_ap_id(activity.data["object"]) | |||
liked_activity_id = if liked_activity, do: liked_activity.id, else: nil | |||
created_at = | |||
activity.data["published"] | |||
|> Utils.date_to_asctime() | |||
text = "#{user.nickname} favorited a status." | |||
favorited_status = | |||
if liked_activity, | |||
do: render("activity.json", Map.merge(opts, %{activity: liked_activity})), | |||
else: nil | |||
%{ | |||
"id" => activity.id, | |||
"user" => UserView.render("show.json", %{user: user, for: opts[:for]}), | |||
"statusnet_html" => text, | |||
"text" => text, | |||
"is_local" => activity.local, | |||
"is_post_verb" => false, | |||
"uri" => "tag:#{activity.data["id"]}:objectType=Favourite", | |||
"created_at" => created_at, | |||
"favorited_status" => favorited_status, | |||
"in_reply_to_status_id" => liked_activity_id, | |||
"external_url" => activity.data["id"], | |||
"activity_type" => "like" | |||
} | |||
end | |||
def render( | |||
"activity.json", | |||
%{activity: %{data: %{"type" => "Create", "object" => object_id}} = activity} = opts | |||
) do | |||
user = get_user(activity.data["actor"], opts) | |||
object = Object.normalize(object_id) | |||
created_at = object.data["published"] |> Utils.date_to_asctime() | |||
like_count = object.data["like_count"] || 0 | |||
announcement_count = object.data["announcement_count"] || 0 | |||
favorited = opts[:for] && opts[:for].ap_id in (object.data["likes"] || []) | |||
repeated = opts[:for] && opts[:for].ap_id in (object.data["announcements"] || []) | |||
pinned = activity.id in user.info.pinned_activities | |||
attentions = | |||
[] | |||
|> Utils.maybe_notify_to_recipients(activity) | |||
|> Utils.maybe_notify_mentioned_recipients(activity) | |||
|> Enum.map(fn ap_id -> get_user(ap_id, opts) end) | |||
|> Enum.filter(& &1) | |||
|> Enum.map(fn user -> UserView.render("show.json", %{user: user, for: opts[:for]}) end) | |||
conversation_id = get_context_id(activity, opts) | |||
tags = object.data["tag"] || [] | |||
possibly_sensitive = object.data["sensitive"] || Enum.member?(tags, "nsfw") | |||
tags = if possibly_sensitive, do: Enum.uniq(["nsfw" | tags]), else: tags | |||
{summary, content} = render_content(object.data) | |||
html = | |||
content | |||
|> HTML.get_cached_scrubbed_html_for_activity( | |||
User.html_filter_policy(opts[:for]), | |||
activity, | |||
"twitterapi:content" | |||
) | |||
|> Formatter.emojify(object.data["emoji"]) | |||
text = | |||
if content do | |||
content | |||
|> String.replace(~r/<br\s?\/?>/, "\n") | |||
|> HTML.get_cached_stripped_html_for_activity(activity, "twitterapi:content") | |||
else | |||
"" | |||
end | |||
reply_parent = Activity.get_in_reply_to_activity(activity) | |||
reply_user = reply_parent && User.get_cached_by_ap_id(reply_parent.actor) | |||
summary = HTML.strip_tags(summary) | |||
card = | |||
StatusView.render( | |||
"card.json", | |||
Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) | |||
) | |||
thread_muted? = | |||
case activity.thread_muted? do | |||
thread_muted? when is_boolean(thread_muted?) -> thread_muted? | |||
nil -> CommonAPI.thread_muted?(user, activity) | |||
end | |||
%{ | |||
"id" => activity.id, | |||
"uri" => object.data["id"], | |||
"user" => UserView.render("show.json", %{user: user, for: opts[:for]}), | |||
"statusnet_html" => html, | |||
"text" => text, | |||
"is_local" => activity.local, | |||
"is_post_verb" => true, | |||
"created_at" => created_at, | |||
"in_reply_to_status_id" => reply_parent && reply_parent.id, | |||
"in_reply_to_screen_name" => reply_user && reply_user.nickname, | |||
"in_reply_to_profileurl" => User.profile_url(reply_user), | |||
"in_reply_to_ostatus_uri" => reply_user && reply_user.ap_id, | |||
"in_reply_to_user_id" => reply_user && reply_user.id, | |||
"statusnet_conversation_id" => conversation_id, | |||
"attachments" => (object.data["attachment"] || []) |> ObjectRepresenter.enum_to_list(opts), | |||
"attentions" => attentions, | |||
"fave_num" => like_count, | |||
"repeat_num" => announcement_count, | |||
"favorited" => !!favorited, | |||
"repeated" => !!repeated, | |||
"pinned" => pinned, | |||
"external_url" => object.data["external_url"] || object.data["id"], | |||
"tags" => tags, | |||
"activity_type" => "post", | |||
"possibly_sensitive" => possibly_sensitive, | |||
"visibility" => Pleroma.Web.ActivityPub.Visibility.get_visibility(object), | |||
"summary" => summary, | |||
"summary_html" => summary |> Formatter.emojify(object.data["emoji"]), | |||
"card" => card, | |||
"muted" => thread_muted? || User.mutes?(opts[:for], user) | |||
} | |||
end | |||
def render("activity.json", %{activity: unhandled_activity}) do | |||
Logger.warn("#{__MODULE__} unhandled activity: #{inspect(unhandled_activity)}") | |||
nil | |||
end | |||
def render_content(%{"type" => "Note"} = object) do | |||
summary = object["summary"] | |||
content = | |||
if !!summary and summary != "" do | |||
"<p>#{summary}</p>#{object["content"]}" | |||
else | |||
object["content"] | |||
end | |||
{summary, content} | |||
end | |||
def render_content(%{"type" => object_type} = object) | |||
when object_type in ["Article", "Page", "Video"] do | |||
summary = object["name"] || object["summary"] | |||
content = | |||
if !!summary and summary != "" and is_bitstring(object["url"]) do | |||
"<p><a href=\"#{object["url"]}\">#{summary}</a></p>#{object["content"]}" | |||
else | |||
object["content"] | |||
end | |||
{summary, content} | |||
end | |||
def render_content(object) do | |||
summary = object["summary"] || "Unhandled activity type: #{object["type"]}" | |||
content = "<p>#{summary}</p>#{object["content"]}" | |||
{summary, content} | |||
end | |||
end |
@@ -1,71 +0,0 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.TwitterAPI.NotificationView do | |||
use Pleroma.Web, :view | |||
alias Pleroma.Notification | |||
alias Pleroma.User | |||
alias Pleroma.Web.CommonAPI.Utils | |||
alias Pleroma.Web.TwitterAPI.ActivityView | |||
alias Pleroma.Web.TwitterAPI.UserView | |||
require Pleroma.Constants | |||
defp get_user(ap_id, opts) do | |||
cond do | |||
user = opts[:users][ap_id] -> | |||
user | |||
String.ends_with?(ap_id, "/followers") -> | |||
nil | |||
ap_id == Pleroma.Constants.as_public() -> | |||
nil | |||
true -> | |||
User.get_cached_by_ap_id(ap_id) | |||
end | |||
end | |||
def render("notification.json", %{notifications: notifications, for: user}) do | |||
render_many( | |||
notifications, | |||
Pleroma.Web.TwitterAPI.NotificationView, | |||
"notification.json", | |||
for: user | |||
) | |||
end | |||
def render( | |||
"notification.json", | |||
%{ | |||
notification: %Notification{ | |||
id: id, | |||
seen: seen, | |||
activity: activity, | |||
inserted_at: created_at | |||
}, | |||
for: user | |||
} = opts | |||
) do | |||
ntype = | |||
case activity.data["type"] do | |||
"Create" -> "mention" | |||
"Like" -> "like" | |||
"Announce" -> "repeat" | |||
"Follow" -> "follow" | |||
end | |||
from = get_user(activity.data["actor"], opts) | |||
%{ | |||
"id" => id, | |||
"ntype" => ntype, | |||
"notice" => ActivityView.render("activity.json", %{activity: activity, for: user}), | |||
"from_profile" => UserView.render("show.json", %{user: from, for: user}), | |||
"is_seen" => if(seen, do: 1, else: 0), | |||
"created_at" => created_at |> Utils.format_naive_asctime() | |||
} | |||
end | |||
end |
@@ -1,191 +0,0 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.TwitterAPI.UserView do | |||
use Pleroma.Web, :view | |||
alias Pleroma.Formatter | |||
alias Pleroma.HTML | |||
alias Pleroma.User | |||
alias Pleroma.Web.CommonAPI.Utils | |||
alias Pleroma.Web.MediaProxy | |||
def render("show.json", %{user: user = %User{}} = assigns) do | |||
render_one(user, Pleroma.Web.TwitterAPI.UserView, "user.json", assigns) | |||
end | |||
def render("index.json", %{users: users, for: user}) do | |||
users | |||
|> render_many(Pleroma.Web.TwitterAPI.UserView, "user.json", for: user) | |||
|> Enum.filter(&Enum.any?/1) | |||
end | |||
def render("user.json", %{user: user = %User{}} = assigns) do | |||
if User.visible_for?(user, assigns[:for]), | |||
do: do_render("user.json", assigns), | |||
else: %{} | |||
end | |||
def render("short.json", %{ | |||
user: %User{ | |||
nickname: nickname, | |||
id: id, | |||
ap_id: ap_id, | |||
name: name | |||
} | |||
}) do | |||
%{ | |||
"fullname" => name, | |||
"id" => id, | |||
"ostatus_uri" => ap_id, | |||
"profile_url" => ap_id, | |||
"screen_name" => nickname | |||
} | |||
end | |||
defp do_render("user.json", %{user: user = %User{}} = assigns) do | |||
for_user = assigns[:for] | |||
image = User.avatar_url(user) |> MediaProxy.url() | |||
{following, follows_you, statusnet_blocking} = | |||
if for_user do | |||
{ | |||
User.following?(for_user, user), | |||
User.following?(user, for_user), | |||
User.blocks?(for_user, user) | |||
} | |||
else | |||
{false, false, false} | |||
end | |||
user_info = User.get_cached_user_info(user) | |||
emoji = | |||
(user.info.source_data["tag"] || []) | |||
|> Enum.filter(fn %{"type" => t} -> t == "Emoji" end) | |||
|> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} -> | |||
{String.trim(name, ":"), url} | |||
end) | |||
emoji = Enum.dedup(emoji ++ user.info.emoji) | |||
description_html = | |||
(user.bio || "") | |||
|> HTML.filter_tags(User.html_filter_policy(for_user)) | |||
|> Formatter.emojify(emoji) | |||
fields = | |||
user.info | |||
|> User.Info.fields() | |||
|> Enum.map(fn %{"name" => name, "value" => value} -> | |||
%{ | |||
"name" => Pleroma.HTML.strip_tags(name), | |||
"value" => Pleroma.HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly) | |||
} | |||
end) | |||
data = | |||
%{ | |||
"created_at" => user.inserted_at |> Utils.format_naive_asctime(), | |||
"description" => HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")), | |||
"description_html" => description_html, | |||
"favourites_count" => 0, | |||
"followers_count" => user_info[:follower_count], | |||
"following" => following, | |||
"follows_you" => follows_you, | |||
"statusnet_blocking" => statusnet_blocking, | |||
"friends_count" => user_info[:following_count], | |||
"id" => user.id, | |||
"name" => user.name || user.nickname, | |||
"name_html" => | |||
if(user.name, | |||
do: HTML.strip_tags(user.name) |> Formatter.emojify(emoji), | |||
else: user.nickname | |||
), | |||
"profile_image_url" => image, | |||
"profile_image_url_https" => image, | |||
"profile_image_url_profile_size" => image, | |||
"profile_image_url_original" => image, | |||
"screen_name" => user.nickname, | |||
"statuses_count" => user_info[:note_count], | |||
"statusnet_profile_url" => user.ap_id, | |||
"cover_photo" => User.banner_url(user) |> MediaProxy.url(), | |||
"background_image" => image_url(user.info.background) |> MediaProxy.url(), | |||
"is_local" => user.local, | |||
"locked" => user.info.locked, | |||
"hide_followers" => user.info.hide_followers, | |||
"hide_follows" => user.info.hide_follows, | |||
"fields" => fields, | |||
# Pleroma extension | |||
"pleroma" => | |||
%{ | |||
"confirmation_pending" => user_info.confirmation_pending, | |||
"tags" => user.tags, | |||
"skip_thread_containment" => user.info.skip_thread_containment | |||
} | |||
|> maybe_with_activation_status(user, for_user) | |||
|> with_notification_settings(user, for_user) | |||
} | |||
|> maybe_with_user_settings(user, for_user) | |||
|> maybe_with_role(user, for_user) | |||
if assigns[:token] do | |||
Map.put(data, "token", token_string(assigns[:token])) | |||
else | |||
data | |||
end | |||
end | |||
defp with_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do | |||
Map.put(data, "notification_settings", user.info.notification_settings) | |||
end | |||
defp with_notification_settings(data, _, _), do: data | |||
defp maybe_with_activation_status(data, user, %User{info: %{is_admin: true}}) do | |||
Map.put(data, "deactivated", user.info.deactivated) | |||
end | |||
defp maybe_with_activation_status(data, _, _), do: data | |||
defp maybe_with_role(data, %User{id: id} = user, %User{id: id}) do | |||
Map.merge(data, %{ | |||
"role" => role(user), | |||
"show_role" => user.info.show_role, | |||
"rights" => %{ | |||
"delete_others_notice" => !!user.info.is_moderator, | |||
"admin" => !!user.info.is_admin | |||
} | |||
}) | |||
end | |||
defp maybe_with_role(data, %User{info: %{show_role: true}} = user, _user) do | |||
Map.merge(data, %{ | |||
"role" => role(user), | |||
"rights" => %{ | |||
"delete_others_notice" => !!user.info.is_moderator, | |||
"admin" => !!user.info.is_admin | |||
} | |||
}) | |||
end | |||
defp maybe_with_role(data, _, _), do: data | |||
defp maybe_with_user_settings(data, %User{info: info, id: id} = _user, %User{id: id}) do | |||
data | |||
|> Kernel.put_in(["default_scope"], info.default_scope) | |||
|> Kernel.put_in(["no_rich_text"], info.no_rich_text) | |||
end | |||
defp maybe_with_user_settings(data, _, _), do: data | |||
defp role(%User{info: %{:is_admin => true}}), do: "admin" | |||
defp role(%User{info: %{:is_moderator => true}}), do: "moderator" | |||
defp role(_), do: "member" | |||
defp image_url(%{"url" => [%{"href" => href} | _]}), do: href | |||
defp image_url(_), do: nil | |||
defp token_string(%Pleroma.Web.OAuth.Token{token: token_str}), do: token_str | |||
defp token_string(token), do: token | |||
end |
@@ -12,7 +12,6 @@ defmodule Pleroma.NotificationTest do | |||
alias Pleroma.Web.ActivityPub.Transmogrifier | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.Streamer | |||
alias Pleroma.Web.TwitterAPI.TwitterAPI | |||
describe "create_notifications" do | |||
test "notifies someone when they are directly addressed" do | |||
@@ -21,7 +20,7 @@ defmodule Pleroma.NotificationTest do | |||
third_user = insert(:user) | |||
{:ok, activity} = | |||
TwitterAPI.create_status(user, %{ | |||
CommonAPI.post(user, %{ | |||
"status" => "hey @#{other_user.nickname} and @#{third_user.nickname}" | |||
}) | |||
@@ -39,7 +38,7 @@ defmodule Pleroma.NotificationTest do | |||
User.subscribe(subscriber, user) | |||
{:ok, status} = TwitterAPI.create_status(user, %{"status" => "Akariiiin"}) | |||
{:ok, status} = CommonAPI.post(user, %{"status" => "Akariiiin"}) | |||
{:ok, [notification]} = Notification.create_notifications(status) | |||
assert notification.user_id == subscriber.id | |||
@@ -184,47 +183,20 @@ defmodule Pleroma.NotificationTest do | |||
test "it doesn't create a notification for follow-unfollow-follow chains" do | |||
user = insert(:user) | |||
followed_user = insert(:user) | |||
{:ok, _, _, activity} = TwitterAPI.follow(user, %{"user_id" => followed_user.id}) | |||
{:ok, _, _, activity} = CommonAPI.follow(user, followed_user) | |||
Notification.create_notification(activity, followed_user) | |||
TwitterAPI.unfollow(user, %{"user_id" => followed_user.id}) | |||
{:ok, _, _, activity_dupe} = TwitterAPI.follow(user, %{"user_id" => followed_user.id}) | |||
CommonAPI.unfollow(user, followed_user) | |||
{:ok, _, _, activity_dupe} = CommonAPI.follow(user, followed_user) | |||
refute Notification.create_notification(activity_dupe, followed_user) | |||
end | |||
test "it doesn't create a notification for like-unlike-like chains" do | |||
user = insert(:user) | |||
liked_user = insert(:user) | |||
{:ok, status} = TwitterAPI.create_status(liked_user, %{"status" => "Yui is best yuru"}) | |||
{:ok, fav_status} = TwitterAPI.fav(user, status.id) | |||
Notification.create_notification(fav_status, liked_user) | |||
TwitterAPI.unfav(user, status.id) | |||
{:ok, dupe} = TwitterAPI.fav(user, status.id) | |||
refute Notification.create_notification(dupe, liked_user) | |||
end | |||
test "it doesn't create a notification for repeat-unrepeat-repeat chains" do | |||
user = insert(:user) | |||
retweeted_user = insert(:user) | |||
{:ok, status} = | |||
TwitterAPI.create_status(retweeted_user, %{ | |||
"status" => "Send dupe notifications to the shadow realm" | |||
}) | |||
{:ok, retweeted_activity} = TwitterAPI.repeat(user, status.id) | |||
Notification.create_notification(retweeted_activity, retweeted_user) | |||
TwitterAPI.unrepeat(user, status.id) | |||
{:ok, dupe} = TwitterAPI.repeat(user, status.id) | |||
refute Notification.create_notification(dupe, retweeted_user) | |||
end | |||
test "it doesn't create duplicate notifications for follow+subscribed users" do | |||
user = insert(:user) | |||
subscriber = insert(:user) | |||
{:ok, _, _, _} = TwitterAPI.follow(subscriber, %{"user_id" => user.id}) | |||
{:ok, _, _, _} = CommonAPI.follow(subscriber, user) | |||
User.subscribe(subscriber, user) | |||
{:ok, status} = TwitterAPI.create_status(user, %{"status" => "Akariiiin"}) | |||
{:ok, status} = CommonAPI.post(user, %{"status" => "Akariiiin"}) | |||
{:ok, [_notif]} = Notification.create_notifications(status) | |||
end | |||
@@ -234,8 +206,7 @@ defmodule Pleroma.NotificationTest do | |||
User.subscribe(subscriber, user) | |||
{:ok, status} = | |||
TwitterAPI.create_status(user, %{"status" => "inwisible", "visibility" => "direct"}) | |||
{:ok, status} = CommonAPI.post(user, %{"status" => "inwisible", "visibility" => "direct"}) | |||
assert {:ok, []} == Notification.create_notifications(status) | |||
end | |||
@@ -246,8 +217,7 @@ defmodule Pleroma.NotificationTest do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
{:ok, activity} = | |||
TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) | |||
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}"}) | |||
{:ok, [notification]} = Notification.create_notifications(activity) | |||
{:ok, notification} = Notification.get(other_user, notification.id) | |||
@@ -259,8 +229,7 @@ defmodule Pleroma.NotificationTest do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
{:ok, activity} = | |||
TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) | |||
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}"}) | |||
{:ok, [notification]} = Notification.create_notifications(activity) | |||
{:error, _notification} = Notification.get(user, notification.id) | |||
@@ -272,8 +241,7 @@ defmodule Pleroma.NotificationTest do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
{:ok, activity} = | |||
TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) | |||
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}"}) | |||
{:ok, [notification]} = Notification.create_notifications(activity) | |||
{:ok, notification} = Notification.dismiss(other_user, notification.id) | |||
@@ -285,8 +253,7 @@ defmodule Pleroma.NotificationTest do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
{:ok, activity} = | |||
TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) | |||
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}"}) | |||
{:ok, [notification]} = Notification.create_notifications(activity) | |||
{:error, _notification} = Notification.dismiss(user, notification.id) | |||
@@ -300,14 +267,14 @@ defmodule Pleroma.NotificationTest do | |||
third_user = insert(:user) | |||
{:ok, activity} = | |||
TwitterAPI.create_status(user, %{ | |||
CommonAPI.post(user, %{ | |||
"status" => "hey @#{other_user.nickname} and @#{third_user.nickname} !" | |||
}) | |||
{:ok, _notifs} = Notification.create_notifications(activity) | |||
{:ok, activity} = | |||
TwitterAPI.create_status(user, %{ | |||
CommonAPI.post(user, %{ | |||
"status" => "hey again @#{other_user.nickname} and @#{third_user.nickname} !" | |||
}) | |||
@@ -325,12 +292,12 @@ defmodule Pleroma.NotificationTest do | |||
other_user = insert(:user) | |||
{:ok, _activity} = | |||
TwitterAPI.create_status(user, %{ | |||
CommonAPI.post(user, %{ | |||
"status" => "hey @#{other_user.nickname}!" | |||
}) | |||
{:ok, _activity} = | |||
TwitterAPI.create_status(user, %{ | |||
CommonAPI.post(user, %{ | |||
"status" => "hey again @#{other_user.nickname}!" | |||
}) | |||
@@ -340,7 +307,7 @@ defmodule Pleroma.NotificationTest do | |||
assert n2.id > n1.id | |||
{:ok, _activity} = | |||
TwitterAPI.create_status(user, %{ | |||
CommonAPI.post(user, %{ | |||
"status" => "hey yet again @#{other_user.nickname}!" | |||
}) | |||
@@ -677,7 +644,7 @@ defmodule Pleroma.NotificationTest do | |||
muted = insert(:user) | |||
{:ok, user} = User.mute(user, muted, false) | |||
{:ok, _activity} = TwitterAPI.create_status(muted, %{"status" => "hey @#{user.nickname}"}) | |||
{:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"}) | |||
assert length(Notification.for_user(user)) == 1 | |||
end | |||
@@ -687,7 +654,7 @@ defmodule Pleroma.NotificationTest do | |||
muted = insert(:user) | |||
{:ok, user} = User.mute(user, muted) | |||
{:ok, _activity} = TwitterAPI.create_status(muted, %{"status" => "hey @#{user.nickname}"}) | |||
{:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"}) | |||
assert Notification.for_user(user) == [] | |||
end | |||
@@ -697,7 +664,7 @@ defmodule Pleroma.NotificationTest do | |||
blocked = insert(:user) | |||
{:ok, user} = User.block(user, blocked) | |||
{:ok, _activity} = TwitterAPI.create_status(blocked, %{"status" => "hey @#{user.nickname}"}) | |||
{:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) | |||
assert Notification.for_user(user) == [] | |||
end | |||
@@ -707,7 +674,7 @@ defmodule Pleroma.NotificationTest do | |||
blocked = insert(:user, ap_id: "http://some-domain.com") | |||
{:ok, user} = User.block_domain(user, "some-domain.com") | |||
{:ok, _activity} = TwitterAPI.create_status(blocked, %{"status" => "hey @#{user.nickname}"}) | |||
{:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) | |||
assert Notification.for_user(user) == [] | |||
end | |||
@@ -716,8 +683,7 @@ defmodule Pleroma.NotificationTest do | |||
user = insert(:user) | |||
another_user = insert(:user) | |||
{:ok, activity} = | |||
TwitterAPI.create_status(another_user, %{"status" => "hey @#{user.nickname}"}) | |||
{:ok, activity} = CommonAPI.post(another_user, %{"status" => "hey @#{user.nickname}"}) | |||
{:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"]) | |||
assert Notification.for_user(user) == [] | |||
@@ -728,7 +694,7 @@ defmodule Pleroma.NotificationTest do | |||
muted = insert(:user) | |||
{:ok, user} = User.mute(user, muted) | |||
{:ok, _activity} = TwitterAPI.create_status(muted, %{"status" => "hey @#{user.nickname}"}) | |||
{:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"}) | |||
assert length(Notification.for_user(user, %{with_muted: true})) == 1 | |||
end | |||
@@ -738,7 +704,7 @@ defmodule Pleroma.NotificationTest do | |||
blocked = insert(:user) | |||
{:ok, user} = User.block(user, blocked) | |||
{:ok, _activity} = TwitterAPI.create_status(blocked, %{"status" => "hey @#{user.nickname}"}) | |||
{:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) | |||
assert length(Notification.for_user(user, %{with_muted: true})) == 1 | |||
end | |||
@@ -748,7 +714,7 @@ defmodule Pleroma.NotificationTest do | |||
blocked = insert(:user, ap_id: "http://some-domain.com") | |||
{:ok, user} = User.block_domain(user, "some-domain.com") | |||
{:ok, _activity} = TwitterAPI.create_status(blocked, %{"status" => "hey @#{user.nickname}"}) | |||
{:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) | |||
assert length(Notification.for_user(user, %{with_muted: true})) == 1 | |||
end | |||
@@ -757,8 +723,7 @@ defmodule Pleroma.NotificationTest do | |||
user = insert(:user) | |||
another_user = insert(:user) | |||
{:ok, activity} = | |||
TwitterAPI.create_status(another_user, %{"status" => "hey @#{user.nickname}"}) | |||
{:ok, activity} = CommonAPI.post(another_user, %{"status" => "hey @#{user.nickname}"}) | |||
{:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"]) | |||
assert length(Notification.for_user(user, %{with_muted: true})) == 1 | |||
@@ -69,8 +69,8 @@ defmodule Pleroma.UserTest do | |||
locked = insert(:user, %{info: %{locked: true}}) | |||
follower = insert(:user) | |||
Pleroma.Web.TwitterAPI.TwitterAPI.follow(follower, %{"user_id" => unlocked.id}) | |||
Pleroma.Web.TwitterAPI.TwitterAPI.follow(follower, %{"user_id" => locked.id}) | |||
CommonAPI.follow(follower, unlocked) | |||
CommonAPI.follow(follower, locked) | |||
assert {:ok, []} = User.get_follow_requests(unlocked) | |||
assert {:ok, [activity]} = User.get_follow_requests(locked) | |||
@@ -83,9 +83,9 @@ defmodule Pleroma.UserTest do | |||
pending_follower = insert(:user) | |||
accepted_follower = insert(:user) | |||
Pleroma.Web.TwitterAPI.TwitterAPI.follow(pending_follower, %{"user_id" => locked.id}) | |||
Pleroma.Web.TwitterAPI.TwitterAPI.follow(pending_follower, %{"user_id" => locked.id}) | |||
Pleroma.Web.TwitterAPI.TwitterAPI.follow(accepted_follower, %{"user_id" => locked.id}) | |||
CommonAPI.follow(pending_follower, locked) | |||
CommonAPI.follow(pending_follower, locked) | |||
CommonAPI.follow(accepted_follower, locked) | |||
User.follow(accepted_follower, locked) | |||
assert {:ok, [activity]} = User.get_follow_requests(locked) | |||
@@ -1279,11 +1279,9 @@ defmodule Pleroma.UserTest do | |||
{:ok, _follower2} = User.follow(follower2, user) | |||
{:ok, _follower3} = User.follow(follower3, user) | |||
{:ok, _} = User.block(user, follower) | |||
{:ok, user} = User.block(user, follower) | |||
user_show = Pleroma.Web.TwitterAPI.UserView.render("show.json", %{user: user}) | |||
assert Map.get(user_show, "followers_count") == 2 | |||
assert User.user_info(user).follower_count == 2 | |||
end | |||
describe "list_inactive_users_query/1" do | |||
@@ -1327,7 +1325,7 @@ defmodule Pleroma.UserTest do | |||
to = Enum.random(users -- [user]) | |||
{:ok, _} = | |||
Pleroma.Web.TwitterAPI.TwitterAPI.create_status(user, %{ | |||
CommonAPI.post(user, %{ | |||
"status" => "hey @#{to.nickname}" | |||
}) | |||
end) | |||
@@ -1359,12 +1357,12 @@ defmodule Pleroma.UserTest do | |||
Enum.each(recipients, fn to -> | |||
{:ok, _} = | |||
Pleroma.Web.TwitterAPI.TwitterAPI.create_status(sender, %{ | |||
CommonAPI.post(sender, %{ | |||
"status" => "hey @#{to.nickname}" | |||
}) | |||
{:ok, _} = | |||
Pleroma.Web.TwitterAPI.TwitterAPI.create_status(sender, %{ | |||
CommonAPI.post(sender, %{ | |||
"status" => "hey again @#{to.nickname}" | |||
}) | |||
end) | |||
@@ -21,7 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
alias Pleroma.Web.OAuth.Token | |||
alias Pleroma.Web.OStatus | |||
alias Pleroma.Web.Push | |||
alias Pleroma.Web.TwitterAPI.TwitterAPI | |||
import Pleroma.Factory | |||
import ExUnit.CaptureLog | |||
import Tesla.Mock | |||
@@ -1484,12 +1483,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
filename: "an_image.jpg" | |||
} | |||
media = | |||
TwitterAPI.upload(file, user, "json") | |||
|> Jason.decode!() | |||
{:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id) | |||
{:ok, image_post} = | |||
CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]}) | |||
{:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]}) | |||
conn = | |||
conn | |||
@@ -8,8 +8,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do | |||
alias Pleroma.Notification | |||
alias Pleroma.ScheduledActivity | |||
alias Pleroma.User | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.MastodonAPI.MastodonAPI | |||
alias Pleroma.Web.TwitterAPI.TwitterAPI | |||
import Pleroma.Factory | |||
@@ -75,8 +75,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do | |||
User.subscribe(subscriber, user) | |||
{:ok, status} = TwitterAPI.create_status(user, %{"status" => "Akariiiin"}) | |||
{:ok, status1} = TwitterAPI.create_status(user, %{"status" => "Magi"}) | |||
{:ok, status} = CommonAPI.post(user, %{"status" => "Akariiiin"}) | |||
{:ok, status1} = CommonAPI.post(user, %{"status" => "Magi"}) | |||
{:ok, [notification]} = Notification.create_notifications(status) | |||
{:ok, [notification1]} = Notification.create_notifications(status1) | |||
res = MastodonAPI.get_notifications(subscriber) | |||
@@ -1,60 +0,0 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.TwitterAPI.Representers.ObjectReprenterTest do | |||
use Pleroma.DataCase | |||
alias Pleroma.Object | |||
alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter | |||
test "represent an image attachment" do | |||
object = %Object{ | |||
id: 5, | |||
data: %{ | |||
"type" => "Image", | |||
"url" => [ | |||
%{ | |||
"mediaType" => "sometype", | |||
"href" => "someurl" | |||
} | |||
], | |||
"uuid" => 6 | |||
} | |||
} | |||
expected_object = %{ | |||
id: 6, | |||
url: "someurl", | |||
mimetype: "sometype", | |||
oembed: false, | |||
description: nil | |||
} | |||
assert expected_object == ObjectRepresenter.to_map(object) | |||
end | |||
test "represents mastodon-style attachments" do | |||
object = %Object{ | |||
id: nil, | |||
data: %{ | |||
"mediaType" => "image/png", | |||
"name" => "blabla", | |||
"type" => "Document", | |||
"url" => | |||
"http://mastodon.example.org/system/media_attachments/files/000/000/001/original/8619f31c6edec470.png" | |||
} | |||
} | |||
expected_object = %{ | |||
url: | |||
"http://mastodon.example.org/system/media_attachments/files/000/000/001/original/8619f31c6edec470.png", | |||
mimetype: "image/png", | |||
oembed: false, | |||
id: nil, | |||
description: "blabla" | |||
} | |||
assert expected_object == ObjectRepresenter.to_map(object) | |||
end | |||
end |
@@ -4,270 +4,17 @@ | |||
defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do | |||
use Pleroma.DataCase | |||
alias Pleroma.Activity | |||
alias Pleroma.Object | |||
alias Pleroma.Repo | |||
alias Pleroma.User | |||
alias Pleroma.UserInviteToken | |||
alias Pleroma.Web.ActivityPub.ActivityPub | |||
alias Pleroma.Web.TwitterAPI.ActivityView | |||
alias Pleroma.Web.MastodonAPI.AccountView | |||
alias Pleroma.Web.TwitterAPI.TwitterAPI | |||
alias Pleroma.Web.TwitterAPI.UserView | |||
import Pleroma.Factory | |||
setup_all do | |||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) | |||
:ok | |||
end | |||
test "create a status" do | |||
user = insert(:user) | |||
mentioned_user = insert(:user, %{nickname: "shp", ap_id: "shp"}) | |||
object_data = %{ | |||
"type" => "Image", | |||
"url" => [ | |||
%{ | |||
"type" => "Link", | |||
"mediaType" => "image/jpg", | |||
"href" => "http://example.org/image.jpg" | |||
} | |||
], | |||
"uuid" => 1 | |||
} | |||
object = Repo.insert!(%Object{data: object_data}) | |||
input = %{ | |||
"status" => | |||
"Hello again, @shp.<script></script>\nThis is on another :firefox: line. #2hu #epic #phantasmagoric", | |||
"media_ids" => [object.id] | |||
} | |||
{:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input) | |||
object = Object.normalize(activity) | |||
expected_text = | |||
"Hello again, <span class='h-card'><a data-user='#{mentioned_user.id}' class='u-url mention' href='shp'>@<span>shp</span></a></span>.<script></script><br>This is on another :firefox: line. <a class='hashtag' data-tag='2hu' href='http://localhost:4001/tag/2hu' rel='tag'>#2hu</a> <a class='hashtag' data-tag='epic' href='http://localhost:4001/tag/epic' rel='tag'>#epic</a> <a class='hashtag' data-tag='phantasmagoric' href='http://localhost:4001/tag/phantasmagoric' rel='tag'>#phantasmagoric</a><br><a href=\"http://example.org/image.jpg\" class='attachment'>image.jpg</a>" | |||
assert get_in(object.data, ["content"]) == expected_text | |||
assert get_in(object.data, ["type"]) == "Note" | |||
assert get_in(object.data, ["actor"]) == user.ap_id | |||
assert get_in(activity.data, ["actor"]) == user.ap_id | |||
assert Enum.member?(get_in(activity.data, ["cc"]), User.ap_followers(user)) | |||
assert Enum.member?( | |||
get_in(activity.data, ["to"]), | |||
"https://www.w3.org/ns/activitystreams#Public" | |||
) | |||
assert Enum.member?(get_in(activity.data, ["to"]), "shp") | |||
assert activity.local == true | |||
assert %{"firefox" => "http://localhost:4001/emoji/Firefox.gif"} = object.data["emoji"] | |||
# hashtags | |||
assert object.data["tag"] == ["2hu", "epic", "phantasmagoric"] | |||
# Add a context | |||
assert is_binary(get_in(activity.data, ["context"])) | |||
assert is_binary(get_in(object.data, ["context"])) | |||
assert is_list(object.data["attachment"]) | |||
assert activity.data["object"] == object.data["id"] | |||
user = User.get_cached_by_ap_id(user.ap_id) | |||
assert user.info.note_count == 1 | |||
end | |||
test "create a status that is a reply" do | |||
user = insert(:user) | |||
input = %{ | |||
"status" => "Hello again." | |||
} | |||
{:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input) | |||
object = Object.normalize(activity) | |||
input = %{ | |||
"status" => "Here's your (you).", | |||
"in_reply_to_status_id" => activity.id | |||
} | |||
{:ok, reply = %Activity{}} = TwitterAPI.create_status(user, input) | |||
reply_object = Object.normalize(reply) | |||
assert get_in(reply.data, ["context"]) == get_in(activity.data, ["context"]) | |||
assert get_in(reply_object.data, ["context"]) == get_in(object.data, ["context"]) | |||
assert get_in(reply_object.data, ["inReplyTo"]) == get_in(activity.data, ["object"]) | |||
assert Activity.get_in_reply_to_activity(reply).id == activity.id | |||
end | |||
test "Follow another user using user_id" do | |||
user = insert(:user) | |||
followed = insert(:user) | |||
{:ok, user, followed, _activity} = TwitterAPI.follow(user, %{"user_id" => followed.id}) | |||
assert User.ap_followers(followed) in user.following | |||
{:ok, _, _, _} = TwitterAPI.follow(user, %{"user_id" => followed.id}) | |||
end | |||
test "Follow another user using screen_name" do | |||
user = insert(:user) | |||
followed = insert(:user) | |||
{:ok, user, followed, _activity} = | |||
TwitterAPI.follow(user, %{"screen_name" => followed.nickname}) | |||
assert User.ap_followers(followed) in user.following | |||
followed = User.get_cached_by_ap_id(followed.ap_id) | |||
assert followed.info.follower_count == 1 | |||
{:ok, _, _, _} = TwitterAPI.follow(user, %{"screen_name" => followed.nickname}) | |||
end | |||
test "Unfollow another user using user_id" do | |||
unfollowed = insert(:user) | |||
user = insert(:user, %{following: [User.ap_followers(unfollowed)]}) | |||
ActivityPub.follow(user, unfollowed) | |||
{:ok, user, unfollowed} = TwitterAPI.unfollow(user, %{"user_id" => unfollowed.id}) | |||
assert user.following == [] | |||
{:error, msg} = TwitterAPI.unfollow(user, %{"user_id" => unfollowed.id}) | |||
assert msg == "Not subscribed!" | |||
end | |||
test "Unfollow another user using screen_name" do | |||
unfollowed = insert(:user) | |||
user = insert(:user, %{following: [User.ap_followers(unfollowed)]}) | |||
ActivityPub.follow(user, unfollowed) | |||
{:ok, user, unfollowed} = TwitterAPI.unfollow(user, %{"screen_name" => unfollowed.nickname}) | |||
assert user.following == [] | |||
{:error, msg} = TwitterAPI.unfollow(user, %{"screen_name" => unfollowed.nickname}) | |||
assert msg == "Not subscribed!" | |||
end | |||
test "Block another user using user_id" do | |||
user = insert(:user) | |||
blocked = insert(:user) | |||
{:ok, user, blocked} = TwitterAPI.block(user, %{"user_id" => blocked.id}) | |||
assert User.blocks?(user, blocked) | |||
end | |||
test "Block another user using screen_name" do | |||
user = insert(:user) | |||
blocked = insert(:user) | |||
{:ok, user, blocked} = TwitterAPI.block(user, %{"screen_name" => blocked.nickname}) | |||
assert User.blocks?(user, blocked) | |||
end | |||
test "Unblock another user using user_id" do | |||
unblocked = insert(:user) | |||
user = insert(:user) | |||
{:ok, user, _unblocked} = TwitterAPI.block(user, %{"user_id" => unblocked.id}) | |||
{:ok, user, _unblocked} = TwitterAPI.unblock(user, %{"user_id" => unblocked.id}) | |||
assert user.info.blocks == [] | |||
end | |||
test "Unblock another user using screen_name" do | |||
unblocked = insert(:user) | |||
user = insert(:user) | |||
{:ok, user, _unblocked} = TwitterAPI.block(user, %{"screen_name" => unblocked.nickname}) | |||
{:ok, user, _unblocked} = TwitterAPI.unblock(user, %{"screen_name" => unblocked.nickname}) | |||
assert user.info.blocks == [] | |||
end | |||
test "upload a file" do | |||
user = insert(:user) | |||
file = %Plug.Upload{ | |||
content_type: "image/jpg", | |||
path: Path.absname("test/fixtures/image.jpg"), | |||
filename: "an_image.jpg" | |||
} | |||
response = TwitterAPI.upload(file, user) | |||
assert is_binary(response) | |||
end | |||
test "it favorites a status, returns the updated activity" do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
note_activity = insert(:note_activity) | |||
{:ok, status} = TwitterAPI.fav(user, note_activity.id) | |||
updated_activity = Activity.get_by_ap_id(note_activity.data["id"]) | |||
assert ActivityView.render("activity.json", %{activity: updated_activity})["fave_num"] == 1 | |||
object = Object.normalize(note_activity) | |||
assert object.data["like_count"] == 1 | |||
assert status == updated_activity | |||
{:ok, _status} = TwitterAPI.fav(other_user, note_activity.id) | |||
object = Object.normalize(note_activity) | |||
assert object.data["like_count"] == 2 | |||
updated_activity = Activity.get_by_ap_id(note_activity.data["id"]) | |||
assert ActivityView.render("activity.json", %{activity: updated_activity})["fave_num"] == 2 | |||
end | |||
test "it unfavorites a status, returns the updated activity" do | |||
user = insert(:user) | |||
note_activity = insert(:note_activity) | |||
object = Object.normalize(note_activity) | |||
{:ok, _like_activity, _object} = ActivityPub.like(user, object) | |||
updated_activity = Activity.get_by_ap_id(note_activity.data["id"]) | |||
assert ActivityView.render("activity.json", activity: updated_activity)["fave_num"] == 1 | |||
{:ok, activity} = TwitterAPI.unfav(user, note_activity.id) | |||
assert ActivityView.render("activity.json", activity: activity)["fave_num"] == 0 | |||
end | |||
test "it retweets a status and returns the retweet" do | |||
user = insert(:user) | |||
note_activity = insert(:note_activity) | |||
{:ok, status} = TwitterAPI.repeat(user, note_activity.id) | |||
updated_activity = Activity.get_by_ap_id(note_activity.data["id"]) | |||
assert status == updated_activity | |||
end | |||
test "it unretweets an already retweeted status" do | |||
user = insert(:user) | |||
note_activity = insert(:note_activity) | |||
{:ok, _status} = TwitterAPI.repeat(user, note_activity.id) | |||
{:ok, status} = TwitterAPI.unrepeat(user, note_activity.id) | |||
updated_activity = Activity.get_by_ap_id(note_activity.data["id"]) | |||
assert status == updated_activity | |||
end | |||
test "it registers a new user and returns the user." do | |||
data = %{ | |||
"nickname" => "lain", | |||
@@ -281,8 +28,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do | |||
fetched_user = User.get_cached_by_nickname("lain") | |||
assert UserView.render("show.json", %{user: user}) == | |||
UserView.render("show.json", %{user: fetched_user}) | |||
assert AccountView.render("account.json", %{user: user}) == | |||
AccountView.render("account.json", %{user: fetched_user}) | |||
end | |||
test "it registers a new user with empty string in bio and returns the user." do | |||
@@ -299,8 +46,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do | |||
fetched_user = User.get_cached_by_nickname("lain") | |||
assert UserView.render("show.json", %{user: user}) == | |||
UserView.render("show.json", %{user: fetched_user}) | |||
assert AccountView.render("account.json", %{user: user}) == | |||
AccountView.render("account.json", %{user: fetched_user}) | |||
end | |||
test "it sends confirmation email if :account_activation_required is specified in instance config" do | |||
@@ -397,8 +144,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do | |||
assert invite.used == true | |||
assert UserView.render("show.json", %{user: user}) == | |||
UserView.render("show.json", %{user: fetched_user}) | |||
assert AccountView.render("account.json", %{user: user}) == | |||
AccountView.render("account.json", %{user: fetched_user}) | |||
end | |||
test "returns error on invalid token" do | |||
@@ -462,8 +209,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do | |||
{:ok, user} = TwitterAPI.register_user(data) | |||
fetched_user = User.get_cached_by_nickname("vinny") | |||
assert UserView.render("show.json", %{user: user}) == | |||
UserView.render("show.json", %{user: fetched_user}) | |||
assert AccountView.render("account.json", %{user: user}) == | |||
AccountView.render("account.json", %{user: fetched_user}) | |||
end | |||
{:ok, data: data, check_fn: check_fn} | |||
@@ -537,8 +284,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do | |||
assert invite.used == true | |||
assert UserView.render("show.json", %{user: user}) == | |||
UserView.render("show.json", %{user: fetched_user}) | |||
assert AccountView.render("account.json", %{user: user}) == | |||
AccountView.render("account.json", %{user: fetched_user}) | |||
data = %{ | |||
"nickname" => "GrimReaper", | |||
@@ -588,8 +335,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do | |||
refute invite.used | |||
assert UserView.render("show.json", %{user: user}) == | |||
UserView.render("show.json", %{user: fetched_user}) | |||
assert AccountView.render("account.json", %{user: user}) == | |||
AccountView.render("account.json", %{user: fetched_user}) | |||
end | |||
test "error after max uses" do | |||
@@ -612,8 +359,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do | |||
invite = Repo.get_by(UserInviteToken, token: invite.token) | |||
assert invite.used == true | |||
assert UserView.render("show.json", %{user: user}) == | |||
UserView.render("show.json", %{user: fetched_user}) | |||
assert AccountView.render("account.json", %{user: user}) == | |||
AccountView.render("account.json", %{user: fetched_user}) | |||
data = %{ | |||
"nickname" => "GrimReaper", | |||
@@ -689,31 +436,9 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do | |||
refute User.get_cached_by_nickname("lain") | |||
end | |||
test "it assigns an integer conversation_id" do | |||
note_activity = insert(:note_activity) | |||
status = ActivityView.render("activity.json", activity: note_activity) | |||
assert is_number(status["statusnet_conversation_id"]) | |||
end | |||
setup do | |||
Supervisor.terminate_child(Pleroma.Supervisor, Cachex) | |||
Supervisor.restart_child(Pleroma.Supervisor, Cachex) | |||
:ok | |||
end | |||
describe "fetching a user by uri" do | |||
test "fetches a user by uri" do | |||
id = "https://mastodon.social/users/lambadalambda" | |||
user = insert(:user) | |||
{:ok, represented} = TwitterAPI.get_external_profile(user, id) | |||
remote = User.get_cached_by_ap_id(id) | |||
assert represented["id"] == UserView.render("show.json", %{user: remote, for: user})["id"] | |||
# Also fetches the feed. | |||
# assert Activity.get_create_by_object_ap_id("tag:mastodon.social,2017-04-05:objectId=1641750:objectType=Status") | |||
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength | |||
end | |||
end | |||
end |
@@ -1,384 +0,0 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do | |||
use Pleroma.DataCase | |||
alias Pleroma.Activity | |||
alias Pleroma.Object | |||
alias Pleroma.Repo | |||
alias Pleroma.User | |||
alias Pleroma.Web.ActivityPub.ActivityPub | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.CommonAPI.Utils | |||
alias Pleroma.Web.TwitterAPI.ActivityView | |||
alias Pleroma.Web.TwitterAPI.UserView | |||
import Pleroma.Factory | |||
import Tesla.Mock | |||
setup do | |||
mock(fn env -> apply(HttpRequestMock, :request, [env]) end) | |||
:ok | |||
end | |||
import Mock | |||
test "returns a temporary ap_id based user for activities missing db users" do | |||
user = insert(:user) | |||
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"}) | |||
Repo.delete(user) | |||
Cachex.clear(:user_cache) | |||
%{"user" => tw_user} = ActivityView.render("activity.json", activity: activity) | |||
assert tw_user["screen_name"] == "erroruser@example.com" | |||
assert tw_user["name"] == user.ap_id | |||
assert tw_user["statusnet_profile_url"] == user.ap_id | |||
end | |||
test "tries to get a user by nickname if fetching by ap_id doesn't work" do | |||
user = insert(:user) | |||
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"}) | |||
{:ok, user} = | |||
user | |||
|> Ecto.Changeset.change(%{ap_id: "#{user.ap_id}/extension/#{user.nickname}"}) | |||
|> Repo.update() | |||
Cachex.clear(:user_cache) | |||
result = ActivityView.render("activity.json", activity: activity) | |||
assert result["user"]["id"] == user.id | |||
end | |||
test "tells if the message is muted for some reason" do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
{:ok, user} = User.mute(user, other_user) | |||
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "test"}) | |||
status = ActivityView.render("activity.json", %{activity: activity}) | |||
assert status["muted"] == false | |||
status = ActivityView.render("activity.json", %{activity: activity, for: user}) | |||
assert status["muted"] == true | |||
end | |||
test "a create activity with a html status" do | |||
text = """ | |||
#Bike log - Commute Tuesday\nhttps://pla.bike/posts/20181211/\n#cycling #CHScycling #commute\nMVIMG_20181211_054020.jpg | |||
""" | |||
{:ok, activity} = CommonAPI.post(insert(:user), %{"status" => text}) | |||
result = ActivityView.render("activity.json", activity: activity) | |||
assert result["statusnet_html"] == | |||
"<a class=\"hashtag\" data-tag=\"bike\" href=\"http://localhost:4001/tag/bike\" rel=\"tag\">#Bike</a> log - Commute Tuesday<br /><a href=\"https://pla.bike/posts/20181211/\">https://pla.bike/posts/20181211/</a><br /><a class=\"hashtag\" data-tag=\"cycling\" href=\"http://localhost:4001/tag/cycling\" rel=\"tag\">#cycling</a> <a class=\"hashtag\" data-tag=\"chscycling\" href=\"http://localhost:4001/tag/chscycling\" rel=\"tag\">#CHScycling</a> <a class=\"hashtag\" data-tag=\"commute\" href=\"http://localhost:4001/tag/commute\" rel=\"tag\">#commute</a><br />MVIMG_20181211_054020.jpg" | |||
assert result["text"] == | |||
"#Bike log - Commute Tuesday\nhttps://pla.bike/posts/20181211/\n#cycling #CHScycling #commute\nMVIMG_20181211_054020.jpg" | |||
end | |||
test "a create activity with a summary containing emoji" do | |||
{:ok, activity} = | |||
CommonAPI.post(insert(:user), %{ | |||
"spoiler_text" => ":firefox: meow", | |||
"status" => "." | |||
}) | |||
result = ActivityView.render("activity.json", activity: activity) | |||
expected = ":firefox: meow" | |||
expected_html = | |||
"<img class=\"emoji\" alt=\"firefox\" title=\"firefox\" src=\"http://localhost:4001/emoji/Firefox.gif\" /> meow" | |||
assert result["summary"] == expected | |||
assert result["summary_html"] == expected_html | |||
end | |||
test "a create activity with a summary containing invalid HTML" do | |||
{:ok, activity} = | |||
CommonAPI.post(insert(:user), %{ | |||
"spoiler_text" => "<span style=\"color: magenta; font-size: 32px;\">meow</span>", | |||
"status" => "." | |||
}) | |||
result = ActivityView.render("activity.json", activity: activity) | |||
expected = "meow" | |||
assert result["summary"] == expected | |||
assert result["summary_html"] == expected | |||
end | |||
test "a create activity with a note" do | |||
user = insert(:user) | |||
other_user = insert(:user, %{nickname: "shp"}) | |||
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"}) | |||
object = Object.normalize(activity) | |||
result = ActivityView.render("activity.json", activity: activity) | |||
convo_id = Utils.context_to_conversation_id(object.data["context"]) | |||
expected = %{ | |||
"activity_type" => "post", | |||
"attachments" => [], | |||
"attentions" => [ | |||
UserView.render("show.json", %{user: other_user}) | |||
], | |||
"created_at" => object.data["published"] |> Utils.date_to_asctime(), | |||
"external_url" => object.data["id"], | |||
"fave_num" => 0, | |||
"favorited" => false, | |||
"id" => activity.id, | |||
"in_reply_to_status_id" => nil, | |||
"in_reply_to_screen_name" => nil, | |||
"in_reply_to_user_id" => nil, | |||
"in_reply_to_profileurl" => nil, | |||
"in_reply_to_ostatus_uri" => nil, | |||
"is_local" => true, | |||
"is_post_verb" => true, | |||
"possibly_sensitive" => false, | |||
"repeat_num" => 0, | |||
"repeated" => false, | |||
"pinned" => false, | |||
"statusnet_conversation_id" => convo_id, | |||
"summary" => "", | |||
"summary_html" => "", | |||
"statusnet_html" => | |||
"Hey <span class=\"h-card\"><a data-user=\"#{other_user.id}\" class=\"u-url mention\" href=\"#{ | |||
other_user.ap_id | |||
}\">@<span>shp</span></a></span>!", | |||
"tags" => [], | |||
"text" => "Hey @shp!", | |||
"uri" => object.data["id"], | |||
"user" => UserView.render("show.json", %{user: user}), | |||
"visibility" => "direct", | |||
"card" => nil, | |||
"muted" => false | |||
} | |||
assert result == expected | |||
end | |||
test "a list of activities" do | |||
user = insert(:user) | |||
other_user = insert(:user, %{nickname: "shp"}) | |||
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"}) | |||
object = Object.normalize(activity) | |||
convo_id = Utils.context_to_conversation_id(object.data["context"]) | |||
mocks = [ | |||
{ | |||
Utils, | |||
[:passthrough], | |||
[context_to_conversation_id: fn _ -> false end] | |||
}, | |||
{ | |||
User, | |||
[:passthrough], | |||
[get_cached_by_ap_id: fn _ -> nil end] | |||
} | |||
] | |||
with_mocks mocks do | |||
[result] = ActivityView.render("index.json", activities: [activity]) | |||
assert result["statusnet_conversation_id"] == convo_id | |||
assert result["user"] | |||
refute called(Utils.context_to_conversation_id(:_)) | |||
refute called(User.get_cached_by_ap_id(user.ap_id)) | |||
refute called(User.get_cached_by_ap_id(other_user.ap_id)) | |||
end | |||
end | |||
test "an activity that is a reply" do | |||
user = insert(:user) | |||
other_user = insert(:user, %{nickname: "shp"}) | |||
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"}) | |||
{:ok, answer} = | |||
CommonAPI.post(other_user, %{"status" => "Hi!", "in_reply_to_status_id" => activity.id}) | |||
result = ActivityView.render("activity.json", %{activity: answer}) | |||
assert result["in_reply_to_status_id"] == activity.id | |||
end | |||
test "a like activity" do | |||
user = insert(:user) | |||
other_user = insert(:user, %{nickname: "shp"}) | |||
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"}) | |||
{:ok, like, _object} = CommonAPI.favorite(activity.id, other_user) | |||
result = ActivityView.render("activity.json", activity: like) | |||
activity = Pleroma.Activity.get_by_ap_id(activity.data["id"]) | |||
expected = %{ | |||
"activity_type" => "like", | |||
"created_at" => like.data["published"] |> Utils.date_to_asctime(), | |||
"external_url" => like.data["id"], | |||
"id" => like.id, | |||
"in_reply_to_status_id" => activity.id, | |||
"is_local" => true, | |||
"is_post_verb" => false, | |||
"favorited_status" => ActivityView.render("activity.json", activity: activity), | |||
"statusnet_html" => "shp favorited a status.", | |||
"text" => "shp favorited a status.", | |||
"uri" => "tag:#{like.data["id"]}:objectType=Favourite", | |||
"user" => UserView.render("show.json", user: other_user) | |||
} | |||
assert result == expected | |||
end | |||
test "a like activity for deleted post" do | |||
user = insert(:user) | |||
other_user = insert(:user, %{nickname: "shp"}) | |||
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"}) | |||
{:ok, like, _object} = CommonAPI.favorite(activity.id, other_user) | |||
CommonAPI.delete(activity.id, user) | |||
result = ActivityView.render("activity.json", activity: like) | |||
expected = %{ | |||
"activity_type" => "like", | |||
"created_at" => like.data["published"] |> Utils.date_to_asctime(), | |||
"external_url" => like.data["id"], | |||
"id" => like.id, | |||
"in_reply_to_status_id" => nil, | |||
"is_local" => true, | |||
"is_post_verb" => false, | |||
"favorited_status" => nil, | |||
"statusnet_html" => "shp favorited a status.", | |||
"text" => "shp favorited a status.", | |||
"uri" => "tag:#{like.data["id"]}:objectType=Favourite", | |||
"user" => UserView.render("show.json", user: other_user) | |||
} | |||
assert result == expected | |||
end | |||
test "an announce activity" do | |||
user = insert(:user) | |||
other_user = insert(:user, %{nickname: "shp"}) | |||
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"}) | |||
{:ok, announce, object} = CommonAPI.repeat(activity.id, other_user) | |||
convo_id = Utils.context_to_conversation_id(object.data["context"]) | |||
activity = Activity.get_by_id(activity.id) | |||
result = ActivityView.render("activity.json", activity: announce) | |||
expected = %{ | |||
"activity_type" => "repeat", | |||
"created_at" => announce.data["published"] |> Utils.date_to_asctime(), | |||
"external_url" => announce.data["id"], | |||
"id" => announce.id, | |||
"is_local" => true, | |||
"is_post_verb" => false, | |||
"statusnet_html" => "shp repeated a status.", | |||
"text" => "shp repeated a status.", | |||
"uri" => "tag:#{announce.data["id"]}:objectType=note", | |||
"user" => UserView.render("show.json", user: other_user), | |||
"retweeted_status" => ActivityView.render("activity.json", activity: activity), | |||
"statusnet_conversation_id" => convo_id | |||
} | |||
assert result == expected | |||
end | |||
test "A follow activity" do | |||
user = insert(:user) | |||
other_user = insert(:user, %{nickname: "shp"}) | |||
{:ok, follower} = User.follow(user, other_user) | |||
{:ok, follow} = ActivityPub.follow(follower, other_user) | |||
result = ActivityView.render("activity.json", activity: follow) | |||
expected = %{ | |||
"activity_type" => "follow", | |||
"attentions" => [], | |||
"created_at" => follow.data["published"] |> Utils.date_to_asctime(), | |||
"external_url" => follow.data["id"], | |||
"id" => follow.id, | |||
"in_reply_to_status_id" => nil, | |||
"is_local" => true, | |||
"is_post_verb" => false, | |||
"statusnet_html" => "#{user.nickname} started following shp", | |||
"text" => "#{user.nickname} started following shp", | |||
"user" => UserView.render("show.json", user: user) | |||
} | |||
assert result == expected | |||
end | |||
test "a delete activity" do | |||
user = insert(:user) | |||
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"}) | |||
{:ok, delete} = CommonAPI.delete(activity.id, user) | |||
result = ActivityView.render("activity.json", activity: delete) | |||
expected = %{ | |||
"activity_type" => "delete", | |||
"attentions" => [], | |||
"created_at" => delete.data["published"] |> Utils.date_to_asctime(), | |||
"external_url" => delete.data["id"], | |||
"id" => delete.id, | |||
"in_reply_to_status_id" => nil, | |||
"is_local" => true, | |||
"is_post_verb" => false, | |||
"statusnet_html" => "deleted notice {{tag", | |||
"text" => "deleted notice {{tag", | |||
"uri" => Object.normalize(delete).data["id"], | |||
"user" => UserView.render("show.json", user: user) | |||
} | |||
assert result == expected | |||
end | |||
test "a peertube video" do | |||
{:ok, object} = | |||
Pleroma.Object.Fetcher.fetch_object_from_id( | |||
"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" | |||
) | |||
%Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"]) | |||
result = ActivityView.render("activity.json", activity: activity) | |||
assert length(result["attachments"]) == 1 | |||
assert result["summary"] == "Friday Night" | |||
end | |||
test "special characters are not escaped in text field for status created" do | |||
text = "<3 is on the way" | |||
{:ok, activity} = CommonAPI.post(insert(:user), %{"status" => text}) | |||
result = ActivityView.render("activity.json", activity: activity) | |||
assert result["text"] == text | |||
end | |||
end |
@@ -1,112 +0,0 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.TwitterAPI.NotificationViewTest do | |||
use Pleroma.DataCase | |||
alias Pleroma.Notification | |||
alias Pleroma.User | |||
alias Pleroma.Web.ActivityPub.ActivityPub | |||
alias Pleroma.Web.CommonAPI.Utils | |||
alias Pleroma.Web.TwitterAPI.ActivityView | |||
alias Pleroma.Web.TwitterAPI.NotificationView | |||
alias Pleroma.Web.TwitterAPI.TwitterAPI | |||
alias Pleroma.Web.TwitterAPI.UserView | |||
import Pleroma.Factory | |||
setup do | |||
user = insert(:user, bio: "<span>Here's some html</span>") | |||
[user: user] | |||
end | |||
test "A follow notification" do | |||
note_activity = insert(:note_activity) | |||
user = User.get_cached_by_ap_id(note_activity.data["actor"]) | |||
follower = insert(:user) | |||
{:ok, follower} = User.follow(follower, user) | |||
{:ok, activity} = ActivityPub.follow(follower, user) | |||
Cachex.put(:user_cache, "user_info:#{user.id}", User.user_info(Repo.get!(User, user.id))) | |||
[follow_notif] = Notification.for_user(user) | |||
represented = %{ | |||
"created_at" => follow_notif.inserted_at |> Utils.format_naive_asctime(), | |||
"from_profile" => UserView.render("show.json", %{user: follower, for: user}), | |||
"id" => follow_notif.id, | |||
"is_seen" => 0, | |||
"notice" => ActivityView.render("activity.json", %{activity: activity, for: user}), | |||
"ntype" => "follow" | |||
} | |||
assert represented == | |||
NotificationView.render("notification.json", %{notification: follow_notif, for: user}) | |||
end | |||
test "A mention notification" do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
{:ok, activity} = | |||
TwitterAPI.create_status(other_user, %{"status" => "Päivää, @#{user.nickname}"}) | |||
[notification] = Notification.for_user(user) | |||
represented = %{ | |||
"created_at" => notification.inserted_at |> Utils.format_naive_asctime(), | |||
"from_profile" => UserView.render("show.json", %{user: other_user, for: user}), | |||
"id" => notification.id, | |||
"is_seen" => 0, | |||
"notice" => ActivityView.render("activity.json", %{activity: activity, for: user}), | |||
"ntype" => "mention" | |||
} | |||
assert represented == | |||
NotificationView.render("notification.json", %{notification: notification, for: user}) | |||
end | |||
test "A retweet notification" do | |||
note_activity = insert(:note_activity) | |||
user = User.get_cached_by_ap_id(note_activity.data["actor"]) | |||
repeater = insert(:user) | |||
{:ok, _activity} = TwitterAPI.repeat(repeater, note_activity.id) | |||
[notification] = Notification.for_user(user) | |||
represented = %{ | |||
"created_at" => notification.inserted_at |> Utils.format_naive_asctime(), | |||
"from_profile" => UserView.render("show.json", %{user: repeater, for: user}), | |||
"id" => notification.id, | |||
"is_seen" => 0, | |||
"notice" => | |||
ActivityView.render("activity.json", %{activity: notification.activity, for: user}), | |||
"ntype" => "repeat" | |||
} | |||
assert represented == | |||
NotificationView.render("notification.json", %{notification: notification, for: user}) | |||
end | |||
test "A like notification" do | |||
note_activity = insert(:note_activity) | |||
user = User.get_cached_by_ap_id(note_activity.data["actor"]) | |||
liker = insert(:user) | |||
{:ok, _activity} = TwitterAPI.fav(liker, note_activity.id) | |||
[notification] = Notification.for_user(user) | |||
represented = %{ | |||
"created_at" => notification.inserted_at |> Utils.format_naive_asctime(), | |||
"from_profile" => UserView.render("show.json", %{user: liker, for: user}), | |||
"id" => notification.id, | |||
"is_seen" => 0, | |||
"notice" => | |||
ActivityView.render("activity.json", %{activity: notification.activity, for: user}), | |||
"ntype" => "like" | |||
} | |||
assert represented == | |||
NotificationView.render("notification.json", %{notification: notification, for: user}) | |||
end | |||
end |
@@ -1,323 +0,0 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.TwitterAPI.UserViewTest do | |||
use Pleroma.DataCase | |||
alias Pleroma.User | |||
alias Pleroma.Web.CommonAPI.Utils | |||
alias Pleroma.Web.TwitterAPI.UserView | |||
import Pleroma.Factory | |||
setup do | |||
user = insert(:user, bio: "<span>Here's some html</span>") | |||
[user: user] | |||
end | |||
test "A user with only a nickname", %{user: user} do | |||
user = %{user | name: nil, nickname: "scarlett@catgirl.science"} | |||
represented = UserView.render("show.json", %{user: user}) | |||
assert represented["name"] == user.nickname | |||
assert represented["name_html"] == user.nickname | |||
end | |||
test "A user with an avatar object", %{user: user} do | |||
image = "image" | |||
user = %{user | avatar: %{"url" => [%{"href" => image}]}} | |||
represented = UserView.render("show.json", %{user: user}) | |||
assert represented["profile_image_url"] == image | |||
end | |||
test "A user with emoji in username" do | |||
expected = | |||
"<img class=\"emoji\" alt=\"karjalanpiirakka\" title=\"karjalanpiirakka\" src=\"/file.png\" /> man" | |||
user = | |||
insert(:user, %{ | |||
info: %{ | |||
source_data: %{ | |||
"tag" => [ | |||
%{ | |||
"type" => "Emoji", | |||
"icon" => %{"url" => "/file.png"}, | |||
"name" => ":karjalanpiirakka:" | |||
} | |||
] | |||
} | |||
}, | |||
name: ":karjalanpiirakka: man" | |||
}) | |||
represented = UserView.render("show.json", %{user: user}) | |||
assert represented["name_html"] == expected | |||
end | |||
test "A user" do | |||
note_activity = insert(:note_activity) | |||
user = User.get_cached_by_ap_id(note_activity.data["actor"]) | |||
{:ok, user} = User.update_note_count(user) | |||
follower = insert(:user) | |||
second_follower = insert(:user) | |||
User.follow(follower, user) | |||
User.follow(second_follower, user) | |||
User.follow(user, follower) | |||
{:ok, user} = User.update_follower_count(user) | |||
Cachex.put(:user_cache, "user_info:#{user.id}", User.user_info(Repo.get!(User, user.id))) | |||
image = "http://localhost:4001/images/avi.png" | |||
banner = "http://localhost:4001/images/banner.png" | |||
represented = %{ | |||
"id" => user.id, | |||
"name" => user.name, | |||
"screen_name" => user.nickname, | |||
"name_html" => user.name, | |||
"description" => HtmlSanitizeEx.strip_tags(user.bio |> String.replace("<br>", "\n")), | |||
"description_html" => HtmlSanitizeEx.basic_html(user.bio), | |||
"created_at" => user.inserted_at |> Utils.format_naive_asctime(), | |||
"favourites_count" => 0, | |||
"statuses_count" => 1, | |||
"friends_count" => 1, | |||
"followers_count" => 2, | |||
"profile_image_url" => image, | |||
"profile_image_url_https" => image, | |||
"profile_image_url_profile_size" => image, | |||
"profile_image_url_original" => image, | |||
"following" => false, | |||
"follows_you" => false, | |||
"statusnet_blocking" => false, | |||
"statusnet_profile_url" => user.ap_id, | |||
"cover_photo" => banner, | |||
"background_image" => nil, | |||
"is_local" => true, | |||
"locked" => false, | |||
"hide_follows" => false, | |||
"hide_followers" => false, | |||
"fields" => [], | |||
"pleroma" => %{ | |||
"confirmation_pending" => false, | |||
"tags" => [], | |||
"skip_thread_containment" => false | |||
}, | |||
"rights" => %{"admin" => false, "delete_others_notice" => false}, | |||
"role" => "member" | |||
} | |||
assert represented == UserView.render("show.json", %{user: user}) | |||
end | |||
test "User exposes settings for themselves and only for themselves", %{user: user} do | |||
as_user = UserView.render("show.json", %{user: user, for: user}) | |||
assert as_user["default_scope"] == user.info.default_scope | |||
assert as_user["no_rich_text"] == user.info.no_rich_text | |||
assert as_user["pleroma"]["notification_settings"] == user.info.notification_settings | |||
as_stranger = UserView.render("show.json", %{user: user}) | |||
refute as_stranger["default_scope"] | |||
refute as_stranger["no_rich_text"] | |||
refute as_stranger["pleroma"]["notification_settings"] | |||
end | |||
test "A user for a given other follower", %{user: user} do | |||
follower = insert(:user, %{following: [User.ap_followers(user)]}) | |||
{:ok, user} = User.update_follower_count(user) | |||
image = "http://localhost:4001/images/avi.png" | |||
banner = "http://localhost:4001/images/banner.png" | |||
represented = %{ | |||
"id" => user.id, | |||
"name" => user.name, | |||
"screen_name" => user.nickname, | |||
"name_html" => user.name, | |||
"description" => HtmlSanitizeEx.strip_tags(user.bio |> String.replace("<br>", "\n")), | |||
"description_html" => HtmlSanitizeEx.basic_html(user.bio), | |||
"created_at" => user.inserted_at |> Utils.format_naive_asctime(), | |||
"favourites_count" => 0, | |||
"statuses_count" => 0, | |||
"friends_count" => 0, | |||
"followers_count" => 1, | |||
"profile_image_url" => image, | |||
"profile_image_url_https" => image, | |||
"profile_image_url_profile_size" => image, | |||
"profile_image_url_original" => image, | |||
"following" => true, | |||
"follows_you" => false, | |||
"statusnet_blocking" => false, | |||
"statusnet_profile_url" => user.ap_id, | |||
"cover_photo" => banner, | |||
"background_image" => nil, | |||
"is_local" => true, | |||
"locked" => false, | |||
"hide_follows" => false, | |||
"hide_followers" => false, | |||
"fields" => [], | |||
"pleroma" => %{ | |||
"confirmation_pending" => false, | |||
"tags" => [], | |||
"skip_thread_containment" => false | |||
}, | |||
"rights" => %{"admin" => false, "delete_others_notice" => false}, | |||
"role" => "member" | |||
} | |||
assert represented == UserView.render("show.json", %{user: user, for: follower}) | |||
end | |||
test "A user that follows you", %{user: user} do | |||
follower = insert(:user) | |||
{:ok, follower} = User.follow(follower, user) | |||
{:ok, user} = User.update_follower_count(user) | |||
image = "http://localhost:4001/images/avi.png" | |||
banner = "http://localhost:4001/images/banner.png" | |||
represented = %{ | |||
"id" => follower.id, | |||
"name" => follower.name, | |||
"screen_name" => follower.nickname, | |||
"name_html" => follower.name, | |||
"description" => HtmlSanitizeEx.strip_tags(follower.bio |> String.replace("<br>", "\n")), | |||
"description_html" => HtmlSanitizeEx.basic_html(follower.bio), | |||
"created_at" => follower.inserted_at |> Utils.format_naive_asctime(), | |||
"favourites_count" => 0, | |||
"statuses_count" => 0, | |||
"friends_count" => 1, | |||
"followers_count" => 0, | |||
"profile_image_url" => image, | |||
"profile_image_url_https" => image, | |||
"profile_image_url_profile_size" => image, | |||
"profile_image_url_original" => image, | |||
"following" => false, | |||
"follows_you" => true, | |||
"statusnet_blocking" => false, | |||
"statusnet_profile_url" => follower.ap_id, | |||
"cover_photo" => banner, | |||
"background_image" => nil, | |||
"is_local" => true, | |||
"locked" => false, | |||
"hide_follows" => false, | |||
"hide_followers" => false, | |||
"fields" => [], | |||
"pleroma" => %{ | |||
"confirmation_pending" => false, | |||
"tags" => [], | |||
"skip_thread_containment" => false | |||
}, | |||
"rights" => %{"admin" => false, "delete_others_notice" => false}, | |||
"role" => "member" | |||
} | |||
assert represented == UserView.render("show.json", %{user: follower, for: user}) | |||
end | |||
test "a user that is a moderator" do | |||
user = insert(:user, %{info: %{is_moderator: true}}) | |||
represented = UserView.render("show.json", %{user: user, for: user}) | |||
assert represented["rights"]["delete_others_notice"] | |||
assert represented["role"] == "moderator" | |||
end | |||
test "a user that is a admin" do | |||
user = insert(:user, %{info: %{is_admin: true}}) | |||
represented = UserView.render("show.json", %{user: user, for: user}) | |||
assert represented["rights"]["admin"] | |||
assert represented["role"] == "admin" | |||
end | |||
test "A moderator with hidden role for another user", %{user: user} do | |||
admin = insert(:user, %{info: %{is_moderator: true, show_role: false}}) | |||
represented = UserView.render("show.json", %{user: admin, for: user}) | |||
assert represented["role"] == nil | |||
end | |||
test "An admin with hidden role for another user", %{user: user} do | |||
admin = insert(:user, %{info: %{is_admin: true, show_role: false}}) | |||
represented = UserView.render("show.json", %{user: admin, for: user}) | |||
assert represented["role"] == nil | |||
end | |||
test "A regular user for the admin", %{user: user} do | |||
admin = insert(:user, %{info: %{is_admin: true}}) | |||
represented = UserView.render("show.json", %{user: user, for: admin}) | |||
assert represented["pleroma"]["deactivated"] == false | |||
end | |||
test "A blocked user for the blocker" do | |||
user = insert(:user) | |||
blocker = insert(:user) | |||
User.block(blocker, user) | |||
image = "http://localhost:4001/images/avi.png" | |||
banner = "http://localhost:4001/images/banner.png" | |||
represented = %{ | |||
"id" => user.id, | |||
"name" => user.name, | |||
"screen_name" => user.nickname, | |||
"name_html" => user.name, | |||
"description" => HtmlSanitizeEx.strip_tags(user.bio |> String.replace("<br>", "\n")), | |||
"description_html" => HtmlSanitizeEx.basic_html(user.bio), | |||
"created_at" => user.inserted_at |> Utils.format_naive_asctime(), | |||
"favourites_count" => 0, | |||
"statuses_count" => 0, | |||
"friends_count" => 0, | |||
"followers_count" => 0, | |||
"profile_image_url" => image, | |||
"profile_image_url_https" => image, | |||
"profile_image_url_profile_size" => image, | |||
"profile_image_url_original" => image, | |||
"following" => false, | |||
"follows_you" => false, | |||
"statusnet_blocking" => true, | |||
"statusnet_profile_url" => user.ap_id, | |||
"cover_photo" => banner, | |||
"background_image" => nil, | |||
"is_local" => true, | |||
"locked" => false, | |||
"hide_follows" => false, | |||
"hide_followers" => false, | |||
"fields" => [], | |||
"pleroma" => %{ | |||
"confirmation_pending" => false, | |||
"tags" => [], | |||
"skip_thread_containment" => false | |||
}, | |||
"rights" => %{"admin" => false, "delete_others_notice" => false}, | |||
"role" => "member" | |||
} | |||
blocker = User.get_cached_by_id(blocker.id) | |||
assert represented == UserView.render("show.json", %{user: user, for: blocker}) | |||
end | |||
test "a user with mastodon fields" do | |||
fields = [ | |||
%{ | |||
"name" => "Pronouns", | |||
"value" => "she/her" | |||
}, | |||
%{ | |||
"name" => "Website", | |||
"value" => "https://example.org/" | |||
} | |||
] | |||
user = | |||
insert(:user, %{ | |||
info: %{ | |||
source_data: %{ | |||
"attachment" => | |||
Enum.map(fields, fn field -> Map.put(field, "type", "PropertyValue") end) | |||
} | |||
} | |||
}) | |||
userview = UserView.render("show.json", %{user: user}) | |||
assert userview["fields"] == fields | |||
end | |||
end |