Merge branch 'update-service-files-of-openrc-and-systemd-to-new-recommended-paths...
[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 alias Pleroma.Web.Salmon
8 alias Pleroma.Web.WebFinger
9 alias Pleroma.User
10 alias Pleroma.Repo
11 alias Pleroma.Web.ActivityPub.ActivityPub
12 alias Pleroma.Web.ActivityPub.Transmogrifier
13 alias Pleroma.Web.ActivityPub.Utils
14 import Ecto.Query
15
16 # the instance itself is not a Person, but instead an Application
17 def render("user.json", %{user: %{nickname: nil} = user}) do
18 {:ok, user} = WebFinger.ensure_keys_present(user)
19 {:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys)
20 public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
21 public_key = :public_key.pem_encode([public_key])
22
23 %{
24 "id" => user.ap_id,
25 "type" => "Application",
26 "following" => "#{user.ap_id}/following",
27 "followers" => "#{user.ap_id}/followers",
28 "inbox" => "#{user.ap_id}/inbox",
29 "name" => "Pleroma",
30 "summary" => "Virtual actor for Pleroma relay",
31 "url" => user.ap_id,
32 "manuallyApprovesFollowers" => false,
33 "publicKey" => %{
34 "id" => "#{user.ap_id}#main-key",
35 "owner" => user.ap_id,
36 "publicKeyPem" => public_key
37 },
38 "endpoints" => %{
39 "sharedInbox" => "#{Pleroma.Web.Endpoint.url()}/inbox"
40 }
41 }
42 |> Map.merge(Utils.make_json_ld_header())
43 end
44
45 def render("user.json", %{user: user}) do
46 {:ok, user} = WebFinger.ensure_keys_present(user)
47 {:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys)
48 public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
49 public_key = :public_key.pem_encode([public_key])
50
51 %{
52 "id" => user.ap_id,
53 "type" => "Person",
54 "following" => "#{user.ap_id}/following",
55 "followers" => "#{user.ap_id}/followers",
56 "inbox" => "#{user.ap_id}/inbox",
57 "outbox" => "#{user.ap_id}/outbox",
58 "preferredUsername" => user.nickname,
59 "name" => user.name,
60 "summary" => user.bio,
61 "url" => user.ap_id,
62 "manuallyApprovesFollowers" => user.info.locked,
63 "publicKey" => %{
64 "id" => "#{user.ap_id}#main-key",
65 "owner" => user.ap_id,
66 "publicKeyPem" => public_key
67 },
68 "endpoints" => %{
69 "sharedInbox" => "#{Pleroma.Web.Endpoint.url()}/inbox"
70 },
71 "icon" => %{
72 "type" => "Image",
73 "url" => User.avatar_url(user)
74 },
75 "image" => %{
76 "type" => "Image",
77 "url" => User.banner_url(user)
78 },
79 "tag" => user.info.source_data["tag"] || []
80 }
81 |> Map.merge(Utils.make_json_ld_header())
82 end
83
84 def render("following.json", %{user: user, page: page}) do
85 query = User.get_friends_query(user)
86 query = from(user in query, select: [:ap_id])
87 following = Repo.all(query)
88
89 collection(following, "#{user.ap_id}/following", page, !user.info.hide_follows)
90 |> Map.merge(Utils.make_json_ld_header())
91 end
92
93 def render("following.json", %{user: user}) do
94 query = User.get_friends_query(user)
95 query = from(user in query, select: [:ap_id])
96 following = Repo.all(query)
97
98 %{
99 "id" => "#{user.ap_id}/following",
100 "type" => "OrderedCollection",
101 "totalItems" => length(following),
102 "first" => collection(following, "#{user.ap_id}/following", 1, !user.info.hide_follows)
103 }
104 |> Map.merge(Utils.make_json_ld_header())
105 end
106
107 def render("followers.json", %{user: user, page: page}) do
108 query = User.get_followers_query(user)
109 query = from(user in query, select: [:ap_id])
110 followers = Repo.all(query)
111
112 collection(followers, "#{user.ap_id}/followers", page, !user.info.hide_followers)
113 |> Map.merge(Utils.make_json_ld_header())
114 end
115
116 def render("followers.json", %{user: user}) do
117 query = User.get_followers_query(user)
118 query = from(user in query, select: [:ap_id])
119 followers = Repo.all(query)
120
121 %{
122 "id" => "#{user.ap_id}/followers",
123 "type" => "OrderedCollection",
124 "totalItems" => length(followers),
125 "first" => collection(followers, "#{user.ap_id}/followers", 1, !user.info.hide_followers)
126 }
127 |> Map.merge(Utils.make_json_ld_header())
128 end
129
130 def render("outbox.json", %{user: user, max_id: max_qid}) do
131 # XXX: technically note_count is wrong for this, but it's better than nothing
132 info = User.user_info(user)
133
134 params = %{
135 "limit" => "10"
136 }
137
138 params =
139 if max_qid != nil do
140 Map.put(params, "max_id", max_qid)
141 else
142 params
143 end
144
145 activities = ActivityPub.fetch_user_activities(user, nil, params)
146 min_id = Enum.at(Enum.reverse(activities), 0).id
147 max_id = Enum.at(activities, 0).id
148
149 collection =
150 Enum.map(activities, fn act ->
151 {:ok, data} = Transmogrifier.prepare_outgoing(act.data)
152 data
153 end)
154
155 iri = "#{user.ap_id}/outbox"
156
157 page = %{
158 "id" => "#{iri}?max_id=#{max_id}",
159 "type" => "OrderedCollectionPage",
160 "partOf" => iri,
161 "totalItems" => info.note_count,
162 "orderedItems" => collection,
163 "next" => "#{iri}?max_id=#{min_id}"
164 }
165
166 if max_qid == nil do
167 %{
168 "id" => iri,
169 "type" => "OrderedCollection",
170 "totalItems" => info.note_count,
171 "first" => page
172 }
173 |> Map.merge(Utils.make_json_ld_header())
174 else
175 page |> Map.merge(Utils.make_json_ld_header())
176 end
177 end
178
179 def render("inbox.json", %{user: user, max_id: max_qid}) do
180 params = %{
181 "limit" => "10"
182 }
183
184 params =
185 if max_qid != nil do
186 Map.put(params, "max_id", max_qid)
187 else
188 params
189 end
190
191 activities = ActivityPub.fetch_activities([user.ap_id | user.following], params)
192
193 min_id = Enum.at(Enum.reverse(activities), 0).id
194 max_id = Enum.at(activities, 0).id
195
196 collection =
197 Enum.map(activities, fn act ->
198 {:ok, data} = Transmogrifier.prepare_outgoing(act.data)
199 data
200 end)
201
202 iri = "#{user.ap_id}/inbox"
203
204 page = %{
205 "id" => "#{iri}?max_id=#{max_id}",
206 "type" => "OrderedCollectionPage",
207 "partOf" => iri,
208 "totalItems" => -1,
209 "orderedItems" => collection,
210 "next" => "#{iri}?max_id=#{min_id}"
211 }
212
213 if max_qid == nil do
214 %{
215 "id" => iri,
216 "type" => "OrderedCollection",
217 "totalItems" => -1,
218 "first" => page
219 }
220 |> Map.merge(Utils.make_json_ld_header())
221 else
222 page |> Map.merge(Utils.make_json_ld_header())
223 end
224 end
225
226 def collection(collection, iri, page, show_items \\ true, total \\ nil) do
227 offset = (page - 1) * 10
228 items = Enum.slice(collection, offset, 10)
229 items = Enum.map(items, fn user -> user.ap_id end)
230 total = total || length(collection)
231
232 map = %{
233 "id" => "#{iri}?page=#{page}",
234 "type" => "OrderedCollectionPage",
235 "partOf" => iri,
236 "totalItems" => total,
237 "orderedItems" => if(show_items, do: items, else: [])
238 }
239
240 if offset < total do
241 Map.put(map, "next", "#{iri}?page=#{page + 1}")
242 else
243 map
244 end
245 end
246 end