Merge branch 'feature/jobs' into 'develop'
[akkoma] / lib / pleroma / web / activity_pub / views / user_view.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.UserView do
6 use Pleroma.Web, :view
7
8 alias Pleroma.Web.WebFinger
9 alias Pleroma.Web.Salmon
10 alias Pleroma.User
11 alias Pleroma.Repo
12 alias Pleroma.Web.ActivityPub.ActivityPub
13 alias Pleroma.Web.ActivityPub.Transmogrifier
14 alias Pleroma.Web.ActivityPub.Utils
15 alias Pleroma.Web.Router.Helpers
16 alias Pleroma.Web.Endpoint
17
18 import Ecto.Query
19
20 def render("endpoints.json", %{user: %User{nickname: nil, local: true} = _user}) do
21 %{"sharedInbox" => Helpers.activity_pub_url(Endpoint, :inbox)}
22 end
23
24 def render("endpoints.json", %{user: %User{local: true} = _user}) do
25 %{
26 "oauthAuthorizationEndpoint" => Helpers.o_auth_url(Endpoint, :authorize),
27 "oauthRegistrationEndpoint" => Helpers.mastodon_api_url(Endpoint, :create_app),
28 "oauthTokenEndpoint" => Helpers.o_auth_url(Endpoint, :token_exchange),
29 "sharedInbox" => Helpers.activity_pub_url(Endpoint, :inbox)
30 }
31 end
32
33 def render("endpoints.json", _), do: %{}
34
35 # the instance itself is not a Person, but instead an Application
36 def render("user.json", %{user: %{nickname: nil} = user}) do
37 {:ok, user} = WebFinger.ensure_keys_present(user)
38 {:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys)
39 public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
40 public_key = :public_key.pem_encode([public_key])
41
42 endpoints = render("endpoints.json", %{user: user})
43
44 %{
45 "id" => user.ap_id,
46 "type" => "Application",
47 "following" => "#{user.ap_id}/following",
48 "followers" => "#{user.ap_id}/followers",
49 "inbox" => "#{user.ap_id}/inbox",
50 "name" => "Pleroma",
51 "summary" => "Virtual actor for Pleroma relay",
52 "url" => user.ap_id,
53 "manuallyApprovesFollowers" => false,
54 "publicKey" => %{
55 "id" => "#{user.ap_id}#main-key",
56 "owner" => user.ap_id,
57 "publicKeyPem" => public_key
58 },
59 "endpoints" => endpoints
60 }
61 |> Map.merge(Utils.make_json_ld_header())
62 end
63
64 def render("user.json", %{user: user}) do
65 {:ok, user} = WebFinger.ensure_keys_present(user)
66 {:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys)
67 public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
68 public_key = :public_key.pem_encode([public_key])
69
70 endpoints = render("endpoints.json", %{user: user})
71
72 %{
73 "id" => user.ap_id,
74 "type" => "Person",
75 "following" => "#{user.ap_id}/following",
76 "followers" => "#{user.ap_id}/followers",
77 "inbox" => "#{user.ap_id}/inbox",
78 "outbox" => "#{user.ap_id}/outbox",
79 "preferredUsername" => user.nickname,
80 "name" => user.name,
81 "summary" => user.bio,
82 "url" => user.ap_id,
83 "manuallyApprovesFollowers" => user.info.locked,
84 "publicKey" => %{
85 "id" => "#{user.ap_id}#main-key",
86 "owner" => user.ap_id,
87 "publicKeyPem" => public_key
88 },
89 "endpoints" => endpoints,
90 "icon" => %{
91 "type" => "Image",
92 "url" => User.avatar_url(user)
93 },
94 "image" => %{
95 "type" => "Image",
96 "url" => User.banner_url(user)
97 },
98 "tag" => user.info.source_data["tag"] || []
99 }
100 |> Map.merge(Utils.make_json_ld_header())
101 end
102
103 def render("following.json", %{user: user, page: page}) do
104 query = User.get_friends_query(user)
105 query = from(user in query, select: [:ap_id])
106 following = Repo.all(query)
107
108 total =
109 if !user.info.hide_follows do
110 length(following)
111 else
112 0
113 end
114
115 collection(following, "#{user.ap_id}/following", page, !user.info.hide_follows, total)
116 |> Map.merge(Utils.make_json_ld_header())
117 end
118
119 def render("following.json", %{user: user}) do
120 query = User.get_friends_query(user)
121 query = from(user in query, select: [:ap_id])
122 following = Repo.all(query)
123
124 total =
125 if !user.info.hide_follows do
126 length(following)
127 else
128 0
129 end
130
131 %{
132 "id" => "#{user.ap_id}/following",
133 "type" => "OrderedCollection",
134 "totalItems" => total,
135 "first" => collection(following, "#{user.ap_id}/following", 1, !user.info.hide_follows)
136 }
137 |> Map.merge(Utils.make_json_ld_header())
138 end
139
140 def render("followers.json", %{user: user, page: page}) do
141 query = User.get_followers_query(user)
142 query = from(user in query, select: [:ap_id])
143 followers = Repo.all(query)
144
145 total =
146 if !user.info.hide_followers do
147 length(followers)
148 else
149 0
150 end
151
152 collection(followers, "#{user.ap_id}/followers", page, !user.info.hide_followers, total)
153 |> Map.merge(Utils.make_json_ld_header())
154 end
155
156 def render("followers.json", %{user: user}) do
157 query = User.get_followers_query(user)
158 query = from(user in query, select: [:ap_id])
159 followers = Repo.all(query)
160
161 total =
162 if !user.info.hide_followers do
163 length(followers)
164 else
165 0
166 end
167
168 %{
169 "id" => "#{user.ap_id}/followers",
170 "type" => "OrderedCollection",
171 "totalItems" => total,
172 "first" =>
173 collection(followers, "#{user.ap_id}/followers", 1, !user.info.hide_followers, total)
174 }
175 |> Map.merge(Utils.make_json_ld_header())
176 end
177
178 def render("outbox.json", %{user: user, max_id: max_qid}) do
179 params = %{
180 "limit" => "10"
181 }
182
183 params =
184 if max_qid != nil do
185 Map.put(params, "max_id", max_qid)
186 else
187 params
188 end
189
190 activities = ActivityPub.fetch_user_activities(user, nil, params)
191 min_id = Enum.at(Enum.reverse(activities), 0).id
192 max_id = Enum.at(activities, 0).id
193
194 collection =
195 Enum.map(activities, fn act ->
196 {:ok, data} = Transmogrifier.prepare_outgoing(act.data)
197 data
198 end)
199
200 iri = "#{user.ap_id}/outbox"
201
202 page = %{
203 "id" => "#{iri}?max_id=#{max_id}",
204 "type" => "OrderedCollectionPage",
205 "partOf" => iri,
206 "orderedItems" => collection,
207 "next" => "#{iri}?max_id=#{min_id}"
208 }
209
210 if max_qid == nil do
211 %{
212 "id" => iri,
213 "type" => "OrderedCollection",
214 "first" => page
215 }
216 |> Map.merge(Utils.make_json_ld_header())
217 else
218 page |> Map.merge(Utils.make_json_ld_header())
219 end
220 end
221
222 def render("inbox.json", %{user: user, max_id: max_qid}) do
223 params = %{
224 "limit" => "10"
225 }
226
227 params =
228 if max_qid != nil do
229 Map.put(params, "max_id", max_qid)
230 else
231 params
232 end
233
234 activities = ActivityPub.fetch_activities([user.ap_id | user.following], params)
235
236 min_id = Enum.at(Enum.reverse(activities), 0).id
237 max_id = Enum.at(activities, 0).id
238
239 collection =
240 Enum.map(activities, fn act ->
241 {:ok, data} = Transmogrifier.prepare_outgoing(act.data)
242 data
243 end)
244
245 iri = "#{user.ap_id}/inbox"
246
247 page = %{
248 "id" => "#{iri}?max_id=#{max_id}",
249 "type" => "OrderedCollectionPage",
250 "partOf" => iri,
251 "orderedItems" => collection,
252 "next" => "#{iri}?max_id=#{min_id}"
253 }
254
255 if max_qid == nil do
256 %{
257 "id" => iri,
258 "type" => "OrderedCollection",
259 "first" => page
260 }
261 |> Map.merge(Utils.make_json_ld_header())
262 else
263 page |> Map.merge(Utils.make_json_ld_header())
264 end
265 end
266
267 def collection(collection, iri, page, show_items \\ true, total \\ nil) do
268 offset = (page - 1) * 10
269 items = Enum.slice(collection, offset, 10)
270 items = Enum.map(items, fn user -> user.ap_id end)
271 total = total || length(collection)
272
273 map = %{
274 "id" => "#{iri}?page=#{page}",
275 "type" => "OrderedCollectionPage",
276 "partOf" => iri,
277 "totalItems" => total,
278 "orderedItems" => if(show_items, do: items, else: [])
279 }
280
281 if offset < total do
282 Map.put(map, "next", "#{iri}?page=#{page + 1}")
283 else
284 map
285 end
286 end
287 end