Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into feature/emojireactv...
[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: AccountView.render("index.json", %{users: users, for: user, as: :user}),
73 me: !!(user && user.ap_id in user_ap_ids)
74 }
75 end
76 end)
77 |> Enum.filter(& &1)
78
79 conn
80 |> json(reactions)
81 else
82 _e ->
83 conn
84 |> json([])
85 end
86 end
87
88 def react_with_emoji(%{assigns: %{user: user}} = conn, %{"id" => activity_id, "emoji" => emoji}) do
89 with {:ok, _activity} <- CommonAPI.react_with_emoji(activity_id, user, emoji),
90 activity <- Activity.get_by_id(activity_id) do
91 conn
92 |> put_view(StatusView)
93 |> render("show.json", %{activity: activity, for: user, as: :activity})
94 end
95 end
96
97 def unreact_with_emoji(%{assigns: %{user: user}} = conn, %{
98 "id" => activity_id,
99 "emoji" => emoji
100 }) do
101 with {:ok, _activity} <-
102 CommonAPI.unreact_with_emoji(activity_id, user, emoji),
103 activity <- Activity.get_by_id(activity_id) do
104 conn
105 |> put_view(StatusView)
106 |> render("show.json", %{activity: activity, for: user, as: :activity})
107 end
108 end
109
110 def conversation(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do
111 with %Participation{} = participation <- Participation.get(participation_id),
112 true <- user.id == participation.user_id do
113 conn
114 |> put_view(ConversationView)
115 |> render("participation.json", %{participation: participation, for: user})
116 else
117 _error ->
118 conn
119 |> put_status(404)
120 |> json(%{"error" => "Unknown conversation id"})
121 end
122 end
123
124 def conversation_statuses(
125 %{assigns: %{user: %{id: user_id} = user}} = conn,
126 %{"id" => participation_id} = params
127 ) do
128 with %Participation{user_id: ^user_id} = participation <-
129 Participation.get(participation_id, preload: [:conversation]) do
130 params =
131 params
132 |> Map.put("blocking_user", user)
133 |> Map.put("muting_user", user)
134 |> Map.put("user", user)
135
136 activities =
137 participation.conversation.ap_id
138 |> ActivityPub.fetch_activities_for_context_query(params)
139 |> Pleroma.Pagination.fetch_paginated(Map.put(params, "total", false))
140 |> Enum.reverse()
141
142 conn
143 |> add_link_headers(activities)
144 |> put_view(StatusView)
145 |> render("index.json",
146 activities: activities,
147 for: user,
148 as: :activity,
149 skip_relationships: skip_relationships?(params)
150 )
151 else
152 _error ->
153 conn
154 |> put_status(404)
155 |> json(%{"error" => "Unknown conversation id"})
156 end
157 end
158
159 def update_conversation(
160 %{assigns: %{user: user}} = conn,
161 %{"id" => participation_id, "recipients" => recipients}
162 ) do
163 with %Participation{} = participation <- Participation.get(participation_id),
164 true <- user.id == participation.user_id,
165 {:ok, participation} <- Participation.set_recipients(participation, recipients) do
166 conn
167 |> put_view(ConversationView)
168 |> render("participation.json", %{participation: participation, for: user})
169 else
170 {:error, message} ->
171 conn
172 |> put_status(:bad_request)
173 |> json(%{"error" => message})
174
175 _error ->
176 conn
177 |> put_status(404)
178 |> json(%{"error" => "Unknown conversation id"})
179 end
180 end
181
182 def mark_conversations_as_read(%{assigns: %{user: user}} = conn, _params) do
183 with {:ok, _, participations} <- Participation.mark_all_as_read(user) do
184 conn
185 |> add_link_headers(participations)
186 |> put_view(ConversationView)
187 |> render("participations.json", participations: participations, for: user)
188 end
189 end
190
191 def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
192 with {:ok, notification} <- Notification.read_one(user, notification_id) do
193 conn
194 |> put_view(NotificationView)
195 |> render("show.json", %{notification: notification, for: user})
196 else
197 {:error, message} ->
198 conn
199 |> put_status(:bad_request)
200 |> json(%{"error" => message})
201 end
202 end
203
204 def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"max_id" => max_id} = params) do
205 with notifications <- Notification.set_read_up_to(user, max_id) do
206 notifications = Enum.take(notifications, 80)
207
208 conn
209 |> put_view(NotificationView)
210 |> render("index.json",
211 notifications: notifications,
212 for: user,
213 skip_relationships: skip_relationships?(params)
214 )
215 end
216 end
217 end