dd1ab3d2b75bbc66fed1ab20fadb3aa6d20782b6
[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 total =
108 if !user.info.hide_follows do
109 length(following)
110 else
111 0
112 end
113
114 collection(following, "#{user.ap_id}/following", page, !user.info.hide_follows, total)
115 |> Map.merge(Utils.make_json_ld_header())
116 end
117
118 def render("following.json", %{user: user}) do
119 query = User.get_friends_query(user)
120 query = from(user in query, select: [:ap_id])
121 following = Repo.all(query)
122 total =
123 if !user.info.hide_follows do
124 length(following)
125 else
126 0
127 end
128
129 %{
130 "id" => "#{user.ap_id}/following",
131 "type" => "OrderedCollection",
132 "totalItems" => total,
133 "first" => collection(following, "#{user.ap_id}/following", 1, !user.info.hide_follows)
134 }
135 |> Map.merge(Utils.make_json_ld_header())
136 end
137
138 def render("followers.json", %{user: user, page: page}) do
139 query = User.get_followers_query(user)
140 query = from(user in query, select: [:ap_id])
141 followers = Repo.all(query)
142 total =
143 if !user.info.hide_followers do
144 length(followers)
145 else
146 0
147 end
148
149 collection(followers, "#{user.ap_id}/followers", page, !user.info.hide_followers, total)
150 |> Map.merge(Utils.make_json_ld_header())
151 end
152
153 def render("followers.json", %{user: user}) do
154 query = User.get_followers_query(user)
155 query = from(user in query, select: [:ap_id])
156 followers = Repo.all(query)
157 total =
158 if !user.info.hide_followers do
159 length(followers)
160 else
161 0
162 end
163
164 %{
165 "id" => "#{user.ap_id}/followers",
166 "type" => "OrderedCollection",
167 "totalItems" => total,
168 "first" => collection(followers, "#{user.ap_id}/followers", 1, !user.info.hide_followers, total)
169 }
170 |> Map.merge(Utils.make_json_ld_header())
171 end
172
173 def render("outbox.json", %{user: user, max_id: max_qid}) do
174 params = %{
175 "limit" => "10"
176 }
177
178 params =
179 if max_qid != nil do
180 Map.put(params, "max_id", max_qid)
181 else
182 params
183 end
184
185 activities = ActivityPub.fetch_user_activities(user, nil, params)
186 min_id = Enum.at(Enum.reverse(activities), 0).id
187 max_id = Enum.at(activities, 0).id
188
189 collection =
190 Enum.map(activities, fn act ->
191 {:ok, data} = Transmogrifier.prepare_outgoing(act.data)
192 data
193 end)
194
195 iri = "#{user.ap_id}/outbox"
196
197 page = %{
198 "id" => "#{iri}?max_id=#{max_id}",
199 "type" => "OrderedCollectionPage",
200 "partOf" => iri,
201 "orderedItems" => collection,
202 "next" => "#{iri}?max_id=#{min_id}"
203 }
204
205 if max_qid == nil do
206 %{
207 "id" => iri,
208 "type" => "OrderedCollection",
209 "first" => page
210 }
211 |> Map.merge(Utils.make_json_ld_header())
212 else
213 page |> Map.merge(Utils.make_json_ld_header())
214 end
215 end
216
217 def render("inbox.json", %{user: user, max_id: max_qid}) do
218 params = %{
219 "limit" => "10"
220 }
221
222 params =
223 if max_qid != nil do
224 Map.put(params, "max_id", max_qid)
225 else
226 params
227 end
228
229 activities = ActivityPub.fetch_activities([user.ap_id | user.following], params)
230
231 min_id = Enum.at(Enum.reverse(activities), 0).id
232 max_id = Enum.at(activities, 0).id
233
234 collection =
235 Enum.map(activities, fn act ->
236 {:ok, data} = Transmogrifier.prepare_outgoing(act.data)
237 data
238 end)
239
240 iri = "#{user.ap_id}/inbox"
241
242 page = %{
243 "id" => "#{iri}?max_id=#{max_id}",
244 "type" => "OrderedCollectionPage",
245 "partOf" => iri,
246 "orderedItems" => collection,
247 "next" => "#{iri}?max_id=#{min_id}"
248 }
249
250 if max_qid == nil do
251 %{
252 "id" => iri,
253 "type" => "OrderedCollection",
254 "first" => page
255 }
256 |> Map.merge(Utils.make_json_ld_header())
257 else
258 page |> Map.merge(Utils.make_json_ld_header())
259 end
260 end
261
262 def collection(collection, iri, page, show_items \\ true, total \\ nil) do
263 offset = (page - 1) * 10
264 items = Enum.slice(collection, offset, 10)
265 items = Enum.map(items, fn user -> user.ap_id end)
266 total = total || length(collection)
267
268 map = %{
269 "id" => "#{iri}?page=#{page}",
270 "type" => "OrderedCollectionPage",
271 "partOf" => iri,
272 "totalItems" => total,
273 "orderedItems" => if(show_items, do: items, else: [])
274 }
275
276 if offset < total do
277 Map.put(map, "next", "#{iri}?page=#{page + 1}")
278 else
279 map
280 end
281 end
282 end