1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.ActivityPub.UserView do
8 alias Pleroma.Web.WebFinger
9 alias Pleroma.Web.Salmon
12 alias Pleroma.Web.ActivityPub.ActivityPub
13 alias Pleroma.Web.ActivityPub.Transmogrifier
14 alias Pleroma.Web.ActivityPub.Utils
18 def render("endpoints.json", %{user: %User{local: true} = _user}) do
20 "oauthAuthorizationEndpoint" => "#{Pleroma.Web.Endpoint.url()}/oauth/authorize",
21 "oauthTokenEndpoint" => "#{Pleroma.Web.Endpoint.url()}/oauth/token"
23 |> Map.merge(render("endpoints.json", %{user: nil}))
26 def render("endpoints.json", _) do
28 "sharedInbox" => "#{Pleroma.Web.Endpoint.url()}/inbox"
32 # the instance itself is not a Person, but instead an Application
33 def render("user.json", %{user: %{nickname: nil} = user}) do
34 {:ok, user} = WebFinger.ensure_keys_present(user)
35 {:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys)
36 public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
37 public_key = :public_key.pem_encode([public_key])
39 endpoints = render("endpoints.json", %{user: user})
43 "type" => "Application",
44 "following" => "#{user.ap_id}/following",
45 "followers" => "#{user.ap_id}/followers",
46 "inbox" => "#{user.ap_id}/inbox",
48 "summary" => "Virtual actor for Pleroma relay",
50 "manuallyApprovesFollowers" => false,
52 "id" => "#{user.ap_id}#main-key",
53 "owner" => user.ap_id,
54 "publicKeyPem" => public_key
56 "endpoints" => endpoints
58 |> Map.merge(Utils.make_json_ld_header())
61 def render("user.json", %{user: user}) do
62 {:ok, user} = WebFinger.ensure_keys_present(user)
63 {:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys)
64 public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
65 public_key = :public_key.pem_encode([public_key])
67 endpoints = render("endpoints.json", %{user: user})
72 "following" => "#{user.ap_id}/following",
73 "followers" => "#{user.ap_id}/followers",
74 "inbox" => "#{user.ap_id}/inbox",
75 "outbox" => "#{user.ap_id}/outbox",
76 "preferredUsername" => user.nickname,
78 "summary" => user.bio,
80 "manuallyApprovesFollowers" => user.info.locked,
82 "id" => "#{user.ap_id}#main-key",
83 "owner" => user.ap_id,
84 "publicKeyPem" => public_key
86 "endpoints" => endpoints,
89 "url" => User.avatar_url(user)
93 "url" => User.banner_url(user)
95 "tag" => user.info.source_data["tag"] || []
97 |> Map.merge(Utils.make_json_ld_header())
100 def render("following.json", %{user: user, page: page}) do
101 query = User.get_friends_query(user)
102 query = from(user in query, select: [:ap_id])
103 following = Repo.all(query)
105 collection(following, "#{user.ap_id}/following", page, !user.info.hide_follows)
106 |> Map.merge(Utils.make_json_ld_header())
109 def render("following.json", %{user: user}) do
110 query = User.get_friends_query(user)
111 query = from(user in query, select: [:ap_id])
112 following = Repo.all(query)
115 "id" => "#{user.ap_id}/following",
116 "type" => "OrderedCollection",
117 "totalItems" => length(following),
118 "first" => collection(following, "#{user.ap_id}/following", 1, !user.info.hide_follows)
120 |> Map.merge(Utils.make_json_ld_header())
123 def render("followers.json", %{user: user, page: page}) do
124 query = User.get_followers_query(user)
125 query = from(user in query, select: [:ap_id])
126 followers = Repo.all(query)
128 collection(followers, "#{user.ap_id}/followers", page, !user.info.hide_followers)
129 |> Map.merge(Utils.make_json_ld_header())
132 def render("followers.json", %{user: user}) do
133 query = User.get_followers_query(user)
134 query = from(user in query, select: [:ap_id])
135 followers = Repo.all(query)
138 "id" => "#{user.ap_id}/followers",
139 "type" => "OrderedCollection",
140 "totalItems" => length(followers),
141 "first" => collection(followers, "#{user.ap_id}/followers", 1, !user.info.hide_followers)
143 |> Map.merge(Utils.make_json_ld_header())
146 def render("outbox.json", %{user: user, max_id: max_qid}) do
147 # XXX: technically note_count is wrong for this, but it's better than nothing
148 info = User.user_info(user)
156 Map.put(params, "max_id", max_qid)
161 activities = ActivityPub.fetch_user_activities(user, nil, params)
162 min_id = Enum.at(Enum.reverse(activities), 0).id
163 max_id = Enum.at(activities, 0).id
166 Enum.map(activities, fn act ->
167 {:ok, data} = Transmogrifier.prepare_outgoing(act.data)
171 iri = "#{user.ap_id}/outbox"
174 "id" => "#{iri}?max_id=#{max_id}",
175 "type" => "OrderedCollectionPage",
177 "totalItems" => info.note_count,
178 "orderedItems" => collection,
179 "next" => "#{iri}?max_id=#{min_id}"
185 "type" => "OrderedCollection",
186 "totalItems" => info.note_count,
189 |> Map.merge(Utils.make_json_ld_header())
191 page |> Map.merge(Utils.make_json_ld_header())
195 def render("inbox.json", %{user: user, max_id: max_qid}) do
202 Map.put(params, "max_id", max_qid)
207 activities = ActivityPub.fetch_activities([user.ap_id | user.following], params)
209 min_id = Enum.at(Enum.reverse(activities), 0).id
210 max_id = Enum.at(activities, 0).id
213 Enum.map(activities, fn act ->
214 {:ok, data} = Transmogrifier.prepare_outgoing(act.data)
218 iri = "#{user.ap_id}/inbox"
221 "id" => "#{iri}?max_id=#{max_id}",
222 "type" => "OrderedCollectionPage",
225 "orderedItems" => collection,
226 "next" => "#{iri}?max_id=#{min_id}"
232 "type" => "OrderedCollection",
236 |> Map.merge(Utils.make_json_ld_header())
238 page |> Map.merge(Utils.make_json_ld_header())
242 def collection(collection, iri, page, show_items \\ true, total \\ nil) do
243 offset = (page - 1) * 10
244 items = Enum.slice(collection, offset, 10)
245 items = Enum.map(items, fn user -> user.ap_id end)
246 total = total || length(collection)
249 "id" => "#{iri}?page=#{page}",
250 "type" => "OrderedCollectionPage",
252 "totalItems" => total,
253 "orderedItems" => if(show_items, do: items, else: [])
257 Map.put(map, "next", "#{iri}?page=#{page + 1}")