use Pleroma.Web, :controller
alias Pleroma.{Repo, Object, Activity, User, Notification, Stats}
alias Pleroma.Web
- alias Pleroma.Web.MastodonAPI.{StatusView, AccountView, MastodonView, ListView, FilterView}
+
+ alias Pleroma.Web.MastodonAPI.{
+ StatusView,
+ AccountView,
+ MastodonView,
+ ListView,
+ FilterView,
+ PushSubscriptionView
+ }
+
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.OAuth.{Authorization, Token, App}
alias Pleroma.Web.MediaProxy
- alias Comeonin.Pbkdf2
+
import Ecto.Query
require Logger
conn
|> add_link_headers(:home_timeline, activities)
- |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity})
+ |> put_view(StatusView)
+ |> render("index.json", %{activities: activities, for: user, as: :activity})
end
def public_timeline(%{assigns: %{user: user}} = conn, params) do
conn
|> add_link_headers(:public_timeline, activities, false, %{"local" => local_only})
- |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity})
+ |> put_view(StatusView)
+ |> render("index.json", %{activities: activities, for: user, as: :activity})
end
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
conn
|> add_link_headers(:user_statuses, activities, params["id"])
- |> render(StatusView, "index.json", %{
+ |> put_view(StatusView)
+ |> render("index.json", %{
activities: activities,
for: reading_user,
as: :activity
conn
|> add_link_headers(:dm_timeline, activities)
- |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity})
+ |> put_view(StatusView)
+ |> render("index.json", %{activities: activities, for: user, as: :activity})
end
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
- try_render(conn, StatusView, "status.json", %{activity: activity, for: user})
+ conn
+ |> put_view(StatusView)
+ |> try_render("status.json", %{activity: activity, for: user})
end
end
{:ok, activity} =
Cachex.fetch!(:idempotency_cache, idempotency_key, fn _ -> CommonAPI.post(user, params) end)
- try_render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
+ conn
+ |> put_view(StatusView)
+ |> try_render("status.json", %{activity: activity, for: user, as: :activity})
end
def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) 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
- try_render(conn, StatusView, "status.json", %{activity: announce, for: user, as: :activity})
+ conn
+ |> put_view(StatusView)
+ |> try_render("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
- try_render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
+ conn
+ |> put_view(StatusView)
+ |> try_render("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
- try_render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
+ conn
+ |> put_view(StatusView)
+ |> try_render("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
- try_render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
+ conn
+ |> put_view(StatusView)
+ |> try_render("status.json", %{activity: activity, for: user, as: :activity})
end
end
id = List.wrap(id)
q = from(u in User, where: u.id in ^id)
targets = Repo.all(q)
- 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([])
+ |> put_view(AccountView)
+ |> render("relationships.json", %{user: user, targets: targets})
end
- def update_media(%{assigns: %{user: _}} = conn, data) do
+ # Instead of returning a 400 when no "id" params is present, Mastodon returns an empty array.
+ def relationships(%{assigns: %{user: _user}} = conn, _), do: json(conn, [])
+
+ def update_media(%{assigns: %{user: user}} = conn, data) do
with %Object{} = object <- Repo.get(Object, data["id"]),
+ true <- Object.authorize_mutation(object, user),
true <- is_binary(data["description"]),
description <- data["description"] do
new_data = %{object.data | "name" => description}
- change = Object.change(object, %{data: new_data})
- {:ok, _} = Repo.update(change)
+ {:ok, _} =
+ object
+ |> Object.change(%{data: new_data})
+ |> Repo.update()
- data =
- new_data
- |> Map.put("id", object.id)
+ attachment_data = Map.put(new_data, "id", object.id)
- render(conn, StatusView, "attachment.json", %{attachment: data})
+ conn
+ |> put_view(StatusView)
+ |> render("attachment.json", %{attachment: attachment_data})
end
end
- def upload(%{assigns: %{user: _}} = conn, %{"file" => file} = data) do
- with {:ok, object} <- ActivityPub.upload(file, description: Map.get(data, "description")) do
- change = Object.change(object, %{data: object.data})
- {:ok, object} = Repo.update(change)
-
- objdata =
- object.data
- |> Map.put("id", object.id)
+ def upload(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do
+ with {:ok, object} <-
+ ActivityPub.upload(file,
+ actor: User.ap_id(user),
+ description: Map.get(data, "description")
+ ) do
+ attachment_data = Map.put(object.data, "id", object.id)
- render(conn, StatusView, "attachment.json", %{attachment: objdata})
+ conn
+ |> put_view(StatusView)
+ |> render("attachment.json", %{attachment: attachment_data})
end
end
with %Activity{data: %{"object" => %{"likes" => likes}}} <- Repo.get(Activity, id) do
q = from(u in User, where: u.ap_id in ^likes)
users = Repo.all(q)
- render(conn, AccountView, "accounts.json", %{users: users, as: :user})
+
+ conn
+ |> put_view(AccountView)
+ |> render(AccountView, "accounts.json", %{users: users, as: :user})
else
_ -> json(conn, [])
end
with %Activity{data: %{"object" => %{"announcements" => announces}}} <- Repo.get(Activity, id) do
q = from(u in User, where: u.ap_id in ^announces)
users = Repo.all(q)
- render(conn, AccountView, "accounts.json", %{users: users, as: :user})
+
+ conn
+ |> put_view(AccountView)
+ |> render("accounts.json", %{users: users, as: :user})
else
_ -> json(conn, [])
end
conn
|> add_link_headers(:hashtag_timeline, activities, params["tag"], %{"local" => local_only})
- |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity})
+ |> put_view(StatusView)
+ |> render("index.json", %{activities: activities, for: user, as: :activity})
end
def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
true -> followers
end
- render(conn, AccountView, "accounts.json", %{users: followers, as: :user})
+ conn
+ |> put_view(AccountView)
+ |> render("accounts.json", %{users: followers, as: :user})
end
end
true -> followers
end
- render(conn, AccountView, "accounts.json", %{users: followers, as: :user})
+ conn
+ |> put_view(AccountView)
+ |> render("accounts.json", %{users: followers, as: :user})
end
end
def follow_requests(%{assigns: %{user: followed}} = conn, _params) do
with {:ok, follow_requests} <- User.get_follow_requests(followed) do
- render(conn, AccountView, "accounts.json", %{users: follow_requests, as: :user})
+ conn
+ |> put_view(AccountView)
+ |> render("accounts.json", %{users: follow_requests, as: :user})
end
end
object: follow_activity.data["id"],
type: "Accept"
}) do
- render(conn, AccountView, "relationship.json", %{user: followed, target: follower})
+ conn
+ |> put_view(AccountView)
+ |> render("relationship.json", %{user: followed, target: follower})
else
{:error, message} ->
conn
object: follow_activity.data["id"],
type: "Reject"
}) do
- render(conn, AccountView, "relationship.json", %{user: followed, target: follower})
+ conn
+ |> put_view(AccountView)
+ |> render("relationship.json", %{user: followed, target: follower})
else
{:error, message} ->
conn
follower,
followed
) do
- render(conn, AccountView, "relationship.json", %{user: follower, target: followed})
+ conn
+ |> put_view(AccountView)
+ |> render("relationship.json", %{user: follower, target: followed})
else
{:error, message} ->
conn
with %User{} = followed <- Repo.get_by(User, nickname: uri),
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
{:ok, _activity} <- ActivityPub.follow(follower, followed) do
- render(conn, AccountView, "account.json", %{user: followed, for: follower})
+ conn
+ |> put_view(AccountView)
+ |> render("account.json", %{user: followed, for: follower})
else
{:error, message} ->
conn
with %User{} = followed <- Repo.get(User, id),
{:ok, _activity} <- ActivityPub.unfollow(follower, followed),
{:ok, follower, _} <- User.unfollow(follower, followed) do
- render(conn, AccountView, "relationship.json", %{user: follower, target: followed})
+ conn
+ |> put_view(AccountView)
+ |> render("relationship.json", %{user: follower, target: followed})
end
end
with %User{} = blocked <- Repo.get(User, id),
{:ok, blocker} <- User.block(blocker, blocked),
{:ok, _activity} <- ActivityPub.block(blocker, blocked) do
- render(conn, AccountView, "relationship.json", %{user: blocker, target: blocked})
+ conn
+ |> put_view(AccountView)
+ |> render("relationship.json", %{user: blocker, target: blocked})
else
{:error, message} ->
conn
with %User{} = blocked <- Repo.get(User, id),
{:ok, blocker} <- User.unblock(blocker, blocked),
{:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do
- render(conn, AccountView, "relationship.json", %{user: blocker, target: blocked})
+ conn
+ |> put_view(AccountView)
+ |> render("relationship.json", %{user: blocker, target: blocked})
else
{:error, message} ->
conn
|> Enum.reverse()
conn
- |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity})
+ |> put_view(StatusView)
+ |> render("index.json", %{activities: activities, for: user, as: :activity})
end
def get_lists(%{assigns: %{user: user}} = conn, opts) do
def list_accounts(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
{:ok, users} = Pleroma.List.get_following(list) do
- render(conn, AccountView, "accounts.json", %{users: users, as: :user})
+ conn
+ |> put_view(AccountView)
+ |> render("accounts.json", %{users: users, as: :user})
end
end
end
def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do
- with %Pleroma.List{title: title, following: following} <- Pleroma.List.get(id, user) do
+ with %Pleroma.List{title: _title, following: following} <- Pleroma.List.get(id, user) do
params =
params
|> Map.put("type", "Create")
|> Enum.reverse()
conn
- |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity})
+ |> put_view(StatusView)
+ |> render("index.json", %{activities: activities, for: user, as: :activity})
else
_e ->
conn
]
},
settings:
- Map.get(user.info, :settings) ||
+ user.info.settings ||
%{
onboarded: true,
home: %{
conn
|> put_layout(false)
- |> render(MastodonView, "index.html", %{initial_state: initial_state})
+ |> put_view(MastodonView)
+ |> render("index.html", %{initial_state: initial_state})
else
conn
|> redirect(to: "/web/login")
end
def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
- with new_info <- Map.put(user.info, "settings", settings),
- change <- User.info_changeset(user, %{info: new_info}),
- {:ok, _user} <- User.update_and_set_cache(change) do
- conn
- |> json(%{})
+ info_cng = User.Info.mastodon_settings_update(user.info, settings)
+
+ with changeset <- Ecto.Changeset.change(user),
+ changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng),
+ {:ok, _user} <- User.update_and_set_cache(changeset) do
+ json(conn, %{})
else
e ->
conn
- |> json(%{error: inspect(e)})
+ |> put_resp_content_type("application/json")
+ |> send_resp(500, Jason.encode!(%{"error" => inspect(e)}))
end
end
Logger.debug("Unimplemented, returning unmodified relationship")
with %User{} = target <- Repo.get(User, id) do
- render(conn, AccountView, "relationship.json", %{user: user, target: target})
+ conn
+ |> put_view(AccountView)
+ |> render("relationship.json", %{user: user, target: target})
end
end
def render_notification(user, %{id: id, activity: activity, inserted_at: created_at} = _params) do
actor = User.get_cached_by_ap_id(activity.data["actor"])
+ parent_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"])
+ mastodon_type = Activity.mastodon_notification_type(activity)
- created_at =
- NaiveDateTime.to_iso8601(created_at)
- |> String.replace(~r/(\.\d+)?$/, ".000Z", global: false)
-
- id = id |> to_string
+ response = %{
+ id: to_string(id),
+ type: mastodon_type,
+ created_at: CommonAPI.Utils.to_masto_date(created_at),
+ account: AccountView.render("account.json", %{user: actor, for: user})
+ }
- case activity.data["type"] do
- "Create" ->
- %{
- id: id,
- type: "mention",
- created_at: created_at,
- account: AccountView.render("account.json", %{user: actor, for: user}),
+ case mastodon_type do
+ "mention" ->
+ response
+ |> Map.merge(%{
status: StatusView.render("status.json", %{activity: activity, for: user})
- }
+ })
- "Like" ->
- liked_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"])
+ "favourite" ->
+ response
+ |> Map.merge(%{
+ status: StatusView.render("status.json", %{activity: parent_activity, for: user})
+ })
- %{
- id: id,
- type: "favourite",
- created_at: created_at,
- account: AccountView.render("account.json", %{user: actor, for: user}),
- status: StatusView.render("status.json", %{activity: liked_activity, for: user})
- }
+ "reblog" ->
+ response
+ |> Map.merge(%{
+ status: StatusView.render("status.json", %{activity: parent_activity, for: user})
+ })
- "Announce" ->
- announced_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"])
-
- %{
- id: id,
- type: "reblog",
- created_at: created_at,
- account: AccountView.render("account.json", %{user: actor, for: user}),
- status: StatusView.render("status.json", %{activity: announced_activity, for: user})
- }
-
- "Follow" ->
- %{
- id: id,
- type: "follow",
- created_at: created_at,
- account: AccountView.render("account.json", %{user: actor, for: user})
- }
+ "follow" ->
+ response
_ ->
nil
json(conn, %{})
end
+ def create_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do
+ true = Pleroma.Web.Push.enabled()
+ Pleroma.Web.Push.Subscription.delete_if_exists(user, token)
+ {:ok, subscription} = Pleroma.Web.Push.Subscription.create(user, token, params)
+ view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
+ json(conn, view)
+ end
+
+ def get_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do
+ true = Pleroma.Web.Push.enabled()
+ subscription = Pleroma.Web.Push.Subscription.get(user, token)
+ view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
+ json(conn, view)
+ end
+
+ def update_push_subscription(
+ %{assigns: %{user: user, token: token}} = conn,
+ params
+ ) do
+ true = Pleroma.Web.Push.enabled()
+ {:ok, subscription} = Pleroma.Web.Push.Subscription.update(user, token, params)
+ view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
+ json(conn, view)
+ end
+
+ def delete_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do
+ true = Pleroma.Web.Push.enabled()
+ {:ok, _response} = Pleroma.Web.Push.Subscription.delete(user, token)
+ json(conn, %{})
+ end
+
def errors(conn, _) do
conn
|> put_status(500)
url = String.replace(api, "{{host}}", host) |> String.replace("{{user}}", user)
with {:ok, %{status: 200, body: body}} <-
- @httpoison.get(url, [], timeout: timeout, recv_timeout: timeout),
+ @httpoison.get(
+ url,
+ [],
+ adapter: [
+ timeout: timeout,
+ recv_timeout: timeout
+ ]
+ ),
{:ok, data} <- Jason.decode(body) do
data2 =
Enum.slice(data, 0, limit)
end
end
- def try_render(conn, renderer, target, params)
+ def try_render(conn, target, params)
when is_binary(target) do
- res = render(conn, renderer, target, params)
+ res = render(conn, target, params)
if res == nil do
conn
end
end
- def try_render(conn, _, _, _) do
+ def try_render(conn, _, _) do
conn
|> put_status(501)
|> json(%{error: "Can't display this activity"})