Merge branch 'develop' into feature/compat/push-subscriptions

# Conflicts:
#	lib/mix/tasks/sample_config.eex
#	lib/pleroma/web/twitter_api/controllers/util_controller.ex
#	mix.exs
#	mix.lock
This commit is contained in:
Egor Kislitsyn 2018-12-06 19:55:58 +07:00
commit 8b4397c704
156 changed files with 4941 additions and 1079 deletions

View file

@ -35,6 +35,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def update_credentials(%{assigns: %{user: user}} = conn, params) do
original_user = user
avatar_upload_limit =
Application.get_env(:pleroma, :instance)
|> Keyword.fetch(:avatar_upload_limit)
banner_upload_limit =
Application.get_env(:pleroma, :instance)
|> Keyword.fetch(:banner_upload_limit)
params =
if bio = params["note"] do
Map.put(params, "bio", bio)
@ -52,7 +60,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
user =
if avatar = params["avatar"] do
with %Plug.Upload{} <- avatar,
{:ok, object} <- ActivityPub.upload(avatar),
{:ok, object} <- ActivityPub.upload(avatar, avatar_upload_limit),
change = Ecto.Changeset.change(user, %{avatar: object.data}),
{:ok, user} = User.update_and_set_cache(change) do
user
@ -66,7 +74,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
user =
if banner = params["header"] do
with %Plug.Upload{} <- banner,
{:ok, object} <- ActivityPub.upload(banner),
{:ok, object} <- ActivityPub.upload(banner, banner_upload_limit),
new_info <- Map.put(user.info, "banner", object.data),
change <- User.info_changeset(user, %{info: new_info}),
{:ok, user} <- User.update_and_set_cache(change) do
@ -124,22 +132,23 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
@instance Application.get_env(:pleroma, :instance)
@mastodon_api_level "2.5.0"
def masto_instance(conn, _params) do
instance = Pleroma.Config.get(:instance)
response = %{
uri: Web.base_url(),
title: Keyword.get(@instance, :name),
description: Keyword.get(@instance, :description),
version: "#{@mastodon_api_level} (compatible; #{Keyword.get(@instance, :version)})",
email: Keyword.get(@instance, :email),
title: Keyword.get(instance, :name),
description: Keyword.get(instance, :description),
version: "#{@mastodon_api_level} (compatible; #{Pleroma.Application.named_version()})",
email: Keyword.get(instance, :email),
urls: %{
streaming_api: String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws")
},
stats: Stats.get_stats(),
thumbnail: Web.base_url() <> "/instance/thumbnail.jpeg",
max_toot_chars: Keyword.get(@instance, :limit)
max_toot_chars: Keyword.get(instance, :limit)
}
json(conn, response)
@ -150,7 +159,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
defp mastodonized_emoji do
Pleroma.Formatter.get_custom_emoji()
Pleroma.Emoji.get_all()
|> Enum.map(fn {shortcode, relative_url} ->
url = to_string(URI.merge(Web.base_url(), relative_url))
@ -223,6 +232,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
activities =
ActivityPub.fetch_activities([user.ap_id | user.following], params)
|> ActivityPub.contain_timeline(user)
|> Enum.reverse()
conn
@ -268,9 +278,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
def dm_timeline(%{assigns: %{user: user}} = conn, _params) do
def dm_timeline(%{assigns: %{user: user}} = conn, params) do
query =
ActivityPub.fetch_activities_query([user.ap_id], %{"type" => "Create", visibility: "direct"})
ActivityPub.fetch_activities_query(
[user.ap_id],
Map.merge(params, %{"type" => "Create", visibility: "direct"})
)
activities = Repo.all(query)
@ -282,7 +295,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Activity{} = activity <- Repo.get(Activity, id),
true <- ActivityPub.visible_for_user?(activity, user) do
render(conn, StatusView, "status.json", %{activity: activity, for: user})
try_render(conn, StatusView, "status.json", %{activity: activity, for: user})
end
end
@ -345,7 +358,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
{:ok, activity} =
Cachex.fetch!(:idempotency_cache, idempotency_key, fn _ -> CommonAPI.post(user, params) end)
render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
try_render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
end
def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
@ -361,28 +374,28 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user) do
render(conn, StatusView, "status.json", %{activity: announce, for: user, as: :activity})
try_render(conn, StatusView, "status.json", %{activity: announce, for: user, as: :activity})
end
end
def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
try_render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
end
end
def fav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user),
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
try_render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
end
end
def unfav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
with {:ok, _, _, %{data: %{"id" => id}}} <- CommonAPI.unfavorite(ap_id_or_id, user),
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
try_render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
end
end
@ -434,6 +447,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
render(conn, AccountView, "relationships.json", %{user: user, targets: targets})
end
# Instead of returning a 400 when no "id" params is present, Mastodon returns an empty array.
def relationships(%{assigns: %{user: user}} = conn, _) do
conn
|> json([])
end
def update_media(%{assigns: %{user: _}} = conn, data) do
with %Object{} = object <- Repo.get(Object, data["id"]),
true <- is_binary(data["description"]),
@ -499,6 +518,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> Map.put("type", "Create")
|> Map.put("local_only", local_only)
|> Map.put("blocking_user", user)
|> Map.put("tag", String.downcase(params["tag"]))
activities =
ActivityPub.fetch_public_activities(params)
@ -574,7 +594,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
with %User{} = followed <- Repo.get(User, id),
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
{:ok, _activity} <- ActivityPub.follow(follower, followed) do
{:ok, _activity} <- ActivityPub.follow(follower, followed),
{:ok, follower, followed} <-
User.wait_and_refresh(
Pleroma.Config.get([:activitypub, :follow_handshake_timeout]),
follower,
followed
) do
render(conn, AccountView, "relationship.json", %{user: follower, target: followed})
else
{:error, message} ->
@ -765,6 +791,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
def account_lists(%{assigns: %{user: user}} = conn, %{"id" => account_id}) do
lists = Pleroma.List.get_lists_account_belongs(user, account_id)
res = ListView.render("lists.json", lists: lists)
json(conn, res)
end
def delete_list(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
{:ok, _list} <- Pleroma.List.delete(list) do
@ -859,6 +891,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
if user && token do
mastodon_emoji = mastodonized_emoji()
limit = Pleroma.Config.get([:instance, :limit])
accounts =
Map.put(%{}, user.id, AccountView.render("account.json", %{user: user, for: user}))
@ -878,7 +912,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
auto_play_gif: false,
display_sensitive_media: false,
reduce_motion: false,
max_toot_chars: Keyword.get(@instance, :limit)
max_toot_chars: limit
},
rights: %{
delete_others_notice: !!user.info["is_moderator"]
@ -938,7 +972,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
push_subscription: nil,
accounts: accounts,
custom_emojis: mastodon_emoji,
char_limit: Keyword.get(@instance, :limit)
char_limit: limit
}
|> Jason.encode!()
@ -964,9 +998,29 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
def login(conn, %{"code" => code}) do
with {:ok, app} <- get_or_make_app(),
%Authorization{} = auth <- Repo.get_by(Authorization, token: code, app_id: app.id),
{:ok, token} <- Token.exchange_token(app, auth) do
conn
|> put_session(:oauth_token, token.token)
|> redirect(to: "/web/getting-started")
end
end
def login(conn, _) do
conn
|> render(MastodonView, "login.html", %{error: false})
with {:ok, app} <- get_or_make_app() do
path =
o_auth_path(conn, :authorize,
response_type: "code",
client_id: app.client_id,
redirect_uri: ".",
scope: app.scopes
)
conn
|> redirect(to: path)
end
end
defp get_or_make_app() do
@ -985,22 +1039,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
def login_post(conn, %{"authorization" => %{"name" => name, "password" => password}}) do
with %User{} = user <- User.get_by_nickname_or_email(name),
true <- Pbkdf2.checkpw(password, user.password_hash),
{:ok, app} <- get_or_make_app(),
{:ok, auth} <- Authorization.create_authorization(app, user),
{:ok, token} <- Token.exchange_token(app, auth) do
conn
|> put_session(:oauth_token, token.token)
|> redirect(to: "/web/getting-started")
else
_e ->
conn
|> render(MastodonView, "login.html", %{error: "Wrong username or password"})
end
end
def logout(conn, _) do
conn
|> clear_session
@ -1173,18 +1211,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> json("Something went wrong")
end
@suggestions Application.get_env(:pleroma, :suggestions)
def suggestions(%{assigns: %{user: user}} = conn, _) do
if Keyword.get(@suggestions, :enabled, false) do
api = Keyword.get(@suggestions, :third_party_engine, "")
timeout = Keyword.get(@suggestions, :timeout, 5000)
limit = Keyword.get(@suggestions, :limit, 23)
suggestions = Pleroma.Config.get(:suggestions)
host =
Application.get_env(:pleroma, Pleroma.Web.Endpoint)
|> Keyword.get(:url)
|> Keyword.get(:host)
if Keyword.get(suggestions, :enabled, false) do
api = Keyword.get(suggestions, :third_party_engine, "")
timeout = Keyword.get(suggestions, :timeout, 5000)
limit = Keyword.get(suggestions, :limit, 23)
host = Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host])
user = user.nickname
url = String.replace(api, "{{host}}", host) |> String.replace("{{user}}", user)
@ -1220,4 +1255,23 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
json(conn, [])
end
end
def try_render(conn, renderer, target, params)
when is_binary(target) do
res = render(conn, renderer, target, params)
if res == nil do
conn
|> put_status(501)
|> json(%{error: "Can't display this activity"})
else
res
end
end
def try_render(conn, _, _, _) do
conn
|> put_status(501)
|> json(%{error: "Can't display this activity"})
end
end

View file

@ -11,9 +11,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonSocket do
timeout: :infinity
)
def connect(params, socket) do
with token when not is_nil(token) <- params["access_token"],
%Token{user_id: user_id} <- Repo.get_by(Token, token: token),
def connect(%{"access_token" => token} = params, socket) do
with %Token{user_id: user_id} <- Repo.get_by(Token, token: token),
%User{} = user <- Repo.get(User, user_id),
stream
when stream in [
@ -26,15 +25,37 @@ defmodule Pleroma.Web.MastodonAPI.MastodonSocket do
"list",
"hashtag"
] <- params["stream"] do
topic = if stream == "list", do: "list:#{params["list"]}", else: stream
socket_stream = if stream == "hashtag", do: "hashtag:#{params["tag"]}", else: stream
topic =
case stream do
"hashtag" -> "hashtag:#{params["tag"]}"
"list" -> "list:#{params["list"]}"
_ -> stream
end
socket =
socket
|> assign(:topic, topic)
|> assign(:user, user)
Pleroma.Web.Streamer.add_socket(socket_stream, socket)
Pleroma.Web.Streamer.add_socket(topic, socket)
{:ok, socket}
else
_e -> :error
end
end
def connect(%{"stream" => stream} = params, socket)
when stream in ["public", "public:local", "hashtag"] do
topic =
case stream do
"hashtag" -> "hashtag:#{params["tag"]}"
_ -> stream
end
with socket =
socket
|> assign(:topic, topic) do
Pleroma.Web.Streamer.add_socket(topic, socket)
{:ok, socket}
else
_e -> :error

View file

@ -72,6 +72,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
end
def render("relationship.json", %{user: user, target: target}) do
follow_activity = Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, target)
requested =
if follow_activity do
follow_activity.data["state"] == "pending"
else
false
end
%{
id: to_string(target.id),
following: User.following?(user, target),
@ -79,7 +88,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
blocking: User.blocks?(user, target),
muting: false,
muting_notifications: false,
requested: false,
requested: requested,
domain_blocking: false,
showing_reblogs: false,
endorsed: false

View file

@ -34,6 +34,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
"status.json",
Map.put(opts, :replied_to_activities, replied_to_activities)
)
|> Enum.filter(fn x -> not is_nil(x) end)
end
def render(
@ -60,7 +61,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
in_reply_to_id: nil,
in_reply_to_account_id: nil,
reblog: reblogged,
content: reblogged[:content],
content: reblogged[:content] || "",
created_at: created_at,
reblogs_count: 0,
replies_count: 0,
@ -158,10 +159,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
}
end
def render("status.json", _) do
nil
end
def render("attachment.json", %{attachment: attachment}) do
[attachment_url | _] = attachment["url"]
media_type = attachment_url["mediaType"] || attachment_url["mimeType"] || "image"
href = attachment_url["href"]
href = attachment_url["href"] |> MediaProxy.url()
type =
cond do
@ -175,9 +180,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
%{
id: to_string(attachment["id"] || hash_id),
url: MediaProxy.url(href),
url: href,
remote_url: href,
preview_url: MediaProxy.url(href),
preview_url: href,
text_url: href,
type: type,
description: attachment["name"]
@ -225,24 +230,24 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
if !!name and name != "" do
"<p><a href=\"#{object["id"]}\">#{name}</a></p>#{object["content"]}"
else
object["content"]
object["content"] || ""
end
content
end
def render_content(%{"type" => "Article"} = object) do
def render_content(%{"type" => object_type} = object) when object_type in ["Article", "Page"] do
summary = object["name"]
content =
if !!summary and summary != "" do
if !!summary and summary != "" and is_bitstring(object["url"]) do
"<p><a href=\"#{object["url"]}\">#{summary}</a></p>#{object["content"]}"
else
object["content"]
object["content"] || ""
end
content
end
def render_content(object), do: object["content"]
def render_content(object), do: object["content"] || ""
end