logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://hacktivis.me/git/pleroma.git

user_view.ex (9433B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.ActivityPub.UserView do
  5. use Pleroma.Web, :view
  6. alias Pleroma.Keys
  7. alias Pleroma.Object
  8. alias Pleroma.Repo
  9. alias Pleroma.User
  10. alias Pleroma.Web.ActivityPub.ObjectView
  11. alias Pleroma.Web.ActivityPub.Transmogrifier
  12. alias Pleroma.Web.ActivityPub.Utils
  13. alias Pleroma.Web.Endpoint
  14. alias Pleroma.Web.Router.Helpers
  15. import Ecto.Query
  16. def render("endpoints.json", %{user: %User{nickname: nil, local: true} = _user}) do
  17. %{"sharedInbox" => Helpers.activity_pub_url(Endpoint, :inbox)}
  18. end
  19. def render("endpoints.json", %{user: %User{local: true} = _user}) do
  20. %{
  21. "oauthAuthorizationEndpoint" => Helpers.o_auth_url(Endpoint, :authorize),
  22. "oauthRegistrationEndpoint" => Helpers.app_url(Endpoint, :create),
  23. "oauthTokenEndpoint" => Helpers.o_auth_url(Endpoint, :token_exchange),
  24. "sharedInbox" => Helpers.activity_pub_url(Endpoint, :inbox),
  25. "uploadMedia" => Helpers.activity_pub_url(Endpoint, :upload_media)
  26. }
  27. end
  28. def render("endpoints.json", _), do: %{}
  29. def render("service.json", %{user: user}) do
  30. {:ok, _, public_key} = Keys.keys_from_pem(user.keys)
  31. public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
  32. public_key = :public_key.pem_encode([public_key])
  33. endpoints = render("endpoints.json", %{user: user})
  34. %{
  35. "id" => user.ap_id,
  36. "type" => "Application",
  37. "following" => "#{user.ap_id}/following",
  38. "followers" => "#{user.ap_id}/followers",
  39. "inbox" => "#{user.ap_id}/inbox",
  40. "outbox" => "#{user.ap_id}/outbox",
  41. "name" => "Pleroma",
  42. "summary" =>
  43. "An internal service actor for this Pleroma instance. No user-serviceable parts inside.",
  44. "url" => user.ap_id,
  45. "manuallyApprovesFollowers" => false,
  46. "publicKey" => %{
  47. "id" => "#{user.ap_id}#main-key",
  48. "owner" => user.ap_id,
  49. "publicKeyPem" => public_key
  50. },
  51. "endpoints" => endpoints,
  52. "invisible" => User.invisible?(user)
  53. }
  54. |> Map.merge(Utils.make_json_ld_header())
  55. end
  56. # the instance itself is not a Person, but instead an Application
  57. def render("user.json", %{user: %User{nickname: nil} = user}),
  58. do: render("service.json", %{user: user})
  59. def render("user.json", %{user: %User{nickname: "internal." <> _} = user}),
  60. do: render("service.json", %{user: user}) |> Map.put("preferredUsername", user.nickname)
  61. def render("user.json", %{user: user}) do
  62. {:ok, _, public_key} = Keys.keys_from_pem(user.keys)
  63. public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
  64. public_key = :public_key.pem_encode([public_key])
  65. user = User.sanitize_html(user)
  66. endpoints = render("endpoints.json", %{user: user})
  67. emoji_tags = Transmogrifier.take_emoji_tags(user)
  68. fields = Enum.map(user.fields, &Map.put(&1, "type", "PropertyValue"))
  69. capabilities =
  70. if is_boolean(user.accepts_chat_messages) do
  71. %{
  72. "acceptsChatMessages" => user.accepts_chat_messages
  73. }
  74. else
  75. %{}
  76. end
  77. birthday =
  78. if user.show_birthday && user.birthday,
  79. do: Date.to_iso8601(user.birthday),
  80. else: nil
  81. %{
  82. "id" => user.ap_id,
  83. "type" => user.actor_type,
  84. "following" => "#{user.ap_id}/following",
  85. "followers" => "#{user.ap_id}/followers",
  86. "inbox" => "#{user.ap_id}/inbox",
  87. "outbox" => "#{user.ap_id}/outbox",
  88. "featured" => "#{user.ap_id}/collections/featured",
  89. "preferredUsername" => user.nickname,
  90. "name" => user.name,
  91. "summary" => user.bio,
  92. "url" => user.ap_id,
  93. "manuallyApprovesFollowers" => user.is_locked,
  94. "publicKey" => %{
  95. "id" => "#{user.ap_id}#main-key",
  96. "owner" => user.ap_id,
  97. "publicKeyPem" => public_key
  98. },
  99. "endpoints" => endpoints,
  100. "attachment" => fields,
  101. "tag" => emoji_tags,
  102. # Note: key name is indeed "discoverable" (not an error)
  103. "discoverable" => user.is_discoverable,
  104. "capabilities" => capabilities,
  105. "alsoKnownAs" => user.also_known_as,
  106. "vcard:bday" => birthday
  107. }
  108. |> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
  109. |> Map.merge(maybe_make_image(&User.banner_url/2, "image", user))
  110. |> Map.merge(Utils.make_json_ld_header())
  111. end
  112. def render("following.json", %{user: user, page: page} = opts) do
  113. showing_items = (opts[:for] && opts[:for] == user) || !user.hide_follows
  114. showing_count = showing_items || !user.hide_follows_count
  115. query = User.get_friends_query(user)
  116. query = from(user in query, select: [:ap_id])
  117. following = Repo.all(query)
  118. total =
  119. if showing_count do
  120. length(following)
  121. else
  122. 0
  123. end
  124. collection(following, "#{user.ap_id}/following", page, showing_items, total)
  125. |> Map.merge(Utils.make_json_ld_header())
  126. end
  127. def render("following.json", %{user: user} = opts) do
  128. showing_items = (opts[:for] && opts[:for] == user) || !user.hide_follows
  129. showing_count = showing_items || !user.hide_follows_count
  130. query = User.get_friends_query(user)
  131. query = from(user in query, select: [:ap_id])
  132. following = Repo.all(query)
  133. total =
  134. if showing_count do
  135. length(following)
  136. else
  137. 0
  138. end
  139. %{
  140. "id" => "#{user.ap_id}/following",
  141. "type" => "OrderedCollection",
  142. "totalItems" => total,
  143. "first" =>
  144. if showing_items do
  145. collection(following, "#{user.ap_id}/following", 1, !user.hide_follows)
  146. else
  147. "#{user.ap_id}/following?page=1"
  148. end
  149. }
  150. |> Map.merge(Utils.make_json_ld_header())
  151. end
  152. def render("followers.json", %{user: user, page: page} = opts) do
  153. showing_items = (opts[:for] && opts[:for] == user) || !user.hide_followers
  154. showing_count = showing_items || !user.hide_followers_count
  155. query = User.get_followers_query(user)
  156. query = from(user in query, select: [:ap_id])
  157. followers = Repo.all(query)
  158. total =
  159. if showing_count do
  160. length(followers)
  161. else
  162. 0
  163. end
  164. collection(followers, "#{user.ap_id}/followers", page, showing_items, total)
  165. |> Map.merge(Utils.make_json_ld_header())
  166. end
  167. def render("followers.json", %{user: user} = opts) do
  168. showing_items = (opts[:for] && opts[:for] == user) || !user.hide_followers
  169. showing_count = showing_items || !user.hide_followers_count
  170. query = User.get_followers_query(user)
  171. query = from(user in query, select: [:ap_id])
  172. followers = Repo.all(query)
  173. total =
  174. if showing_count do
  175. length(followers)
  176. else
  177. 0
  178. end
  179. %{
  180. "id" => "#{user.ap_id}/followers",
  181. "type" => "OrderedCollection",
  182. "first" =>
  183. if showing_items do
  184. collection(followers, "#{user.ap_id}/followers", 1, showing_items, total)
  185. else
  186. "#{user.ap_id}/followers?page=1"
  187. end
  188. }
  189. |> maybe_put_total_items(showing_count, total)
  190. |> Map.merge(Utils.make_json_ld_header())
  191. end
  192. def render("activity_collection.json", %{iri: iri}) do
  193. %{
  194. "id" => iri,
  195. "type" => "OrderedCollection",
  196. "first" => "#{iri}?page=true"
  197. }
  198. |> Map.merge(Utils.make_json_ld_header())
  199. end
  200. def render("activity_collection_page.json", %{
  201. activities: activities,
  202. iri: iri,
  203. pagination: pagination
  204. }) do
  205. collection =
  206. Enum.map(activities, fn activity ->
  207. {:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
  208. data
  209. end)
  210. %{
  211. "type" => "OrderedCollectionPage",
  212. "partOf" => iri,
  213. "orderedItems" => collection
  214. }
  215. |> Map.merge(Utils.make_json_ld_header())
  216. |> Map.merge(pagination)
  217. end
  218. def render("featured.json", %{
  219. user: %{featured_address: featured_address, pinned_objects: pinned_objects}
  220. }) do
  221. objects =
  222. pinned_objects
  223. |> Enum.sort_by(fn {_, pinned_at} -> pinned_at end, &>=/2)
  224. |> Enum.map(fn {id, _} ->
  225. ObjectView.render("object.json", %{object: Object.get_cached_by_ap_id(id)})
  226. end)
  227. %{
  228. "id" => featured_address,
  229. "type" => "OrderedCollection",
  230. "orderedItems" => objects,
  231. "totalItems" => length(objects)
  232. }
  233. |> Map.merge(Utils.make_json_ld_header())
  234. end
  235. defp maybe_put_total_items(map, false, _total), do: map
  236. defp maybe_put_total_items(map, true, total) do
  237. Map.put(map, "totalItems", total)
  238. end
  239. def collection(collection, iri, page, show_items \\ true, total \\ nil) do
  240. offset = (page - 1) * 10
  241. items = Enum.slice(collection, offset, 10)
  242. items = Enum.map(items, fn user -> user.ap_id end)
  243. total = total || length(collection)
  244. map = %{
  245. "id" => "#{iri}?page=#{page}",
  246. "type" => "OrderedCollectionPage",
  247. "partOf" => iri,
  248. "totalItems" => total,
  249. "orderedItems" => if(show_items, do: items, else: [])
  250. }
  251. if offset < total do
  252. Map.put(map, "next", "#{iri}?page=#{page + 1}")
  253. else
  254. map
  255. end
  256. end
  257. defp maybe_make_image(func, key, user) do
  258. if image = func.(user, no_default: true) do
  259. %{
  260. key => %{
  261. "type" => "Image",
  262. "url" => image
  263. }
  264. }
  265. else
  266. %{}
  267. end
  268. end
  269. end