Merge remote-tracking branch 'remotes/origin/develop' into restricted-relations-embedding
[akkoma] / lib / pleroma / web / pleroma_api / controllers / pleroma_api_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
6 use Pleroma.Web, :controller
7
8 import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2, skip_relationships?: 1]
9
10 alias Pleroma.Activity
11 alias Pleroma.Conversation.Participation
12 alias Pleroma.Notification
13 alias Pleroma.Object
14 alias Pleroma.Plugs.OAuthScopesPlug
15 alias Pleroma.User
16 alias Pleroma.Web.ActivityPub.ActivityPub
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.MastodonAPI.AccountView
19 alias Pleroma.Web.MastodonAPI.ConversationView
20 alias Pleroma.Web.MastodonAPI.NotificationView
21 alias Pleroma.Web.MastodonAPI.StatusView
22
23 plug(
24 OAuthScopesPlug,
25 %{scopes: ["read:statuses"]}
26 when action in [:conversation, :conversation_statuses]
27 )
28
29 plug(
30 OAuthScopesPlug,
31 %{scopes: ["read:statuses"], fallback: :proceed_unauthenticated}
32 when action == :emoji_reactions_by
33 )
34
35 plug(
36 OAuthScopesPlug,
37 %{scopes: ["write:statuses"]}
38 when action in [:react_with_emoji, :unreact_with_emoji]
39 )
40
41 plug(
42 OAuthScopesPlug,
43 %{scopes: ["write:conversations"]}
44 when action in [:update_conversation, :mark_conversations_as_read]
45 )
46
47 plug(
48 OAuthScopesPlug,
49 %{scopes: ["write:notifications"]} when action == :mark_notifications_as_read
50 )
51
52 def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id} = params) do
53 with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
54 %Object{data: %{"reactions" => emoji_reactions}} when is_list(emoji_reactions) <-
55 Object.normalize(activity) do
56 reactions =
57 emoji_reactions
58 |> Enum.map(fn [emoji, user_ap_ids] ->
59 if params["emoji"] && params["emoji"] != emoji do
60 nil
61 else
62 users =
63 Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1)
64 |> Enum.filter(fn
65 %{deactivated: false} -> true
66 _ -> false
67 end)
68
69 %{
70 name: emoji,
71 count: length(users),
72 accounts:
73 AccountView.render("index.json", %{
74 users: users,
75 for: user,
76 as: :user,
77 skip_relationships: true
78 }),
79 me: !!(user && user.ap_id in user_ap_ids)
80 }
81 end
82 end)
83 |> Enum.filter(& &1)
84
85 conn
86 |> json(reactions)
87 else
88 _e ->
89 conn
90 |> json([])
91 end
92 end
93
94 def react_with_emoji(%{assigns: %{user: user}} = conn, %{"id" => activity_id, "emoji" => emoji}) do
95 with {:ok, _activity} <- CommonAPI.react_with_emoji(activity_id, user, emoji),
96 activity <- Activity.get_by_id(activity_id) do
97 conn
98 |> put_view(StatusView)
99 |> render("show.json", %{activity: activity, for: user, as: :activity})
100 end
101 end
102
103 def unreact_with_emoji(%{assigns: %{user: user}} = conn, %{
104 "id" => activity_id,
105 "emoji" => emoji
106 }) do
107 with {:ok, _activity} <-
108 CommonAPI.unreact_with_emoji(activity_id, user, emoji),
109 activity <- Activity.get_by_id(activity_id) do
110 conn
111 |> put_view(StatusView)
112 |> render("show.json", %{activity: activity, for: user, as: :activity})
113 end
114 end
115
116 def conversation(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do
117 with %Participation{} = participation <- Participation.get(participation_id),
118 true <- user.id == participation.user_id do
119 conn
120 |> put_view(ConversationView)
121 |> render("participation.json", %{participation: participation, for: user})
122 else
123 _error ->
124 conn
125 |> put_status(404)
126 |> json(%{"error" => "Unknown conversation id"})
127 end
128 end
129
130 def conversation_statuses(
131 %{assigns: %{user: %{id: user_id} = user}} = conn,
132 %{"id" => participation_id} = params
133 ) do
134 with %Participation{user_id: ^user_id} = participation <-
135 Participation.get(participation_id, preload: [:conversation]) do
136 params =
137 params
138 |> Map.put("blocking_user", user)
139 |> Map.put("muting_user", user)
140 |> Map.put("user", user)
141
142 activities =
143 participation.conversation.ap_id
144 |> ActivityPub.fetch_activities_for_context_query(params)
145 |> Pleroma.Pagination.fetch_paginated(Map.put(params, "total", false))
146 |> Enum.reverse()
147
148 conn
149 |> add_link_headers(activities)
150 |> put_view(StatusView)
151 |> render("index.json",
152 activities: activities,
153 for: user,
154 as: :activity,
155 skip_relationships: skip_relationships?(params)
156 )
157 else
158 _error ->
159 conn
160 |> put_status(404)
161 |> json(%{"error" => "Unknown conversation id"})
162 end
163 end
164
165 def update_conversation(
166 %{assigns: %{user: user}} = conn,
167 %{"id" => participation_id, "recipients" => recipients}
168 ) do
169 with %Participation{} = participation <- Participation.get(participation_id),
170 true <- user.id == participation.user_id,
171 {:ok, participation} <- Participation.set_recipients(participation, recipients) do
172 conn
173 |> put_view(ConversationView)
174 |> render("participation.json", %{participation: participation, for: user})
175 else
176 {:error, message} ->
177 conn
178 |> put_status(:bad_request)
179 |> json(%{"error" => message})
180
181 _error ->
182 conn
183 |> put_status(404)
184 |> json(%{"error" => "Unknown conversation id"})
185 end
186 end
187
188 def mark_conversations_as_read(%{assigns: %{user: user}} = conn, _params) do
189 with {:ok, _, participations} <- Participation.mark_all_as_read(user) do
190 conn
191 |> add_link_headers(participations)
192 |> put_view(ConversationView)
193 |> render("participations.json", participations: participations, for: user)
194 end
195 end
196
197 def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
198 with {:ok, notification} <- Notification.read_one(user, notification_id) do
199 conn
200 |> put_view(NotificationView)
201 |> render("show.json", %{notification: notification, for: user})
202 else
203 {:error, message} ->
204 conn
205 |> put_status(:bad_request)
206 |> json(%{"error" => message})
207 end
208 end
209
210 def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"max_id" => max_id} = params) do
211 with notifications <- Notification.set_read_up_to(user, max_id) do
212 notifications = Enum.take(notifications, 80)
213
214 conn
215 |> put_view(NotificationView)
216 |> render("index.json",
217 notifications: notifications,
218 for: user,
219 skip_relationships: skip_relationships?(params)
220 )
221 end
222 end
223 end