X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fweb%2Factivity_pub%2Fviews%2Fuser_view.ex;h=d9c1bcb2c4998ae7b8dfe0c1ed3d197c7f82b19a;hb=1f6ac7680d1ae07be7c7dfd81a8cec2ba52f1c82;hp=9ff1f0c774dc421b3c1f1a77cdab9a0294058e7f;hpb=2c1c221301ee91a8bccc1da2dc658b0f5a5598c9;p=akkoma diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 9ff1f0c77..d9c1bcb2c 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -1,17 +1,78 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.UserView do use Pleroma.Web, :view - alias Pleroma.Web.Salmon - alias Pleroma.Web.WebFinger + + alias Pleroma.Keys + alias Pleroma.Repo alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Utils + alias Pleroma.Web.Endpoint + alias Pleroma.Web.Router.Helpers + + import Ecto.Query + + def render("endpoints.json", %{user: %User{nickname: nil, local: true} = _user}) do + %{"sharedInbox" => Helpers.activity_pub_url(Endpoint, :inbox)} + end + + def render("endpoints.json", %{user: %User{local: true} = _user}) do + %{ + "oauthAuthorizationEndpoint" => Helpers.o_auth_url(Endpoint, :authorize), + "oauthRegistrationEndpoint" => Helpers.mastodon_api_url(Endpoint, :create_app), + "oauthTokenEndpoint" => Helpers.o_auth_url(Endpoint, :token_exchange), + "sharedInbox" => Helpers.activity_pub_url(Endpoint, :inbox) + } + end + + def render("endpoints.json", _), do: %{} + + # the instance itself is not a Person, but instead an Application + def render("user.json", %{user: %{nickname: nil} = user}) do + {:ok, user} = User.ensure_keys_present(user) + {:ok, _, public_key} = Keys.keys_from_pem(user.info.keys) + public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key) + public_key = :public_key.pem_encode([public_key]) + + endpoints = render("endpoints.json", %{user: user}) + + %{ + "id" => user.ap_id, + "type" => "Application", + "following" => "#{user.ap_id}/following", + "followers" => "#{user.ap_id}/followers", + "inbox" => "#{user.ap_id}/inbox", + "name" => "Pleroma", + "summary" => "Virtual actor for Pleroma relay", + "url" => user.ap_id, + "manuallyApprovesFollowers" => false, + "publicKey" => %{ + "id" => "#{user.ap_id}#main-key", + "owner" => user.ap_id, + "publicKeyPem" => public_key + }, + "endpoints" => endpoints + } + |> Map.merge(Utils.make_json_ld_header()) + end def render("user.json", %{user: user}) do - {:ok, user} = WebFinger.ensure_keys_present(user) - {:ok, _, public_key} = Salmon.keys_from_pem(user.info["keys"]) - public_key = :public_key.pem_entry_encode(:RSAPublicKey, public_key) + {:ok, user} = User.ensure_keys_present(user) + {:ok, _, public_key} = Keys.keys_from_pem(user.info.keys) + public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key) public_key = :public_key.pem_encode([public_key]) + + endpoints = render("endpoints.json", %{user: user}) + + user_tags = + user + |> Transmogrifier.add_emoji_tags() + |> Map.get("tag", []) + %{ "id" => user.ap_id, "type" => "Person", @@ -23,118 +84,201 @@ defmodule Pleroma.Web.ActivityPub.UserView do "name" => user.name, "summary" => user.bio, "url" => user.ap_id, - "manuallyApprovesFollowers" => false, + "manuallyApprovesFollowers" => user.info.locked, "publicKey" => %{ "id" => "#{user.ap_id}#main-key", "owner" => user.ap_id, "publicKeyPem" => public_key }, - "endpoints" => %{ - "sharedInbox" => "#{Pleroma.Web.Endpoint.url}/inbox" - }, - "icon" => %{ - "type" => "Image", - "url" => User.avatar_url(user) - }, - "image" => %{ - "type" => "Image", - "url" => User.banner_url(user) - } + "endpoints" => endpoints, + "tag" => (user.info.source_data["tag"] || []) ++ user_tags } + |> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user)) + |> Map.merge(maybe_make_image(&User.banner_url/2, "image", user)) |> Map.merge(Utils.make_json_ld_header()) end - def collection(collection, iri, page) do - offset = (page - 1) * 10 - items = Enum.slice(collection, offset, 10) - items = Enum.map(items, fn (user) -> user.ap_id end) - map = %{ - "id" => "#{iri}?page=#{page}", - "type" => "OrderedCollectionPage", - "partOf" => iri, - "totalItems" => length(collection), - "orderedItems" => items - } - if offset < length(collection) do - Map.put(map, "next", "#{iri}?page=#{page+1}") - end - end + def render("following.json", %{user: user, page: page} = opts) do + showing = (opts[:for] && opts[:for] == user) || !user.info.hide_follows + query = User.get_friends_query(user) + query = from(user in query, select: [:ap_id]) + following = Repo.all(query) + + total = + if showing do + length(following) + else + 0 + end - def render("following.json", %{user: user, page: page}) do - {:ok, following} = User.get_friends(user) - collection(following, "#{user.ap_id}/following", page) + collection(following, "#{user.ap_id}/following", page, showing, total) |> Map.merge(Utils.make_json_ld_header()) end - def render("following.json", %{user: user}) do - {:ok, following} = User.get_friends(user) + def render("following.json", %{user: user} = opts) do + showing = (opts[:for] && opts[:for] == user) || !user.info.hide_follows + query = User.get_friends_query(user) + query = from(user in query, select: [:ap_id]) + following = Repo.all(query) + + total = + if showing do + length(following) + else + 0 + end + %{ "id" => "#{user.ap_id}/following", "type" => "OrderedCollection", - "totalItems" => length(following), - "first" => collection(following, "#{user.ap_id}/following", 1) + "totalItems" => total, + "first" => + if showing do + collection(following, "#{user.ap_id}/following", 1, !user.info.hide_follows) + else + "#{user.ap_id}/following?page=1" + end } |> Map.merge(Utils.make_json_ld_header()) end - def render("followers.json", %{user: user, page: page}) do - {:ok, followers} = User.get_followers(user) - collection(followers, "#{user.ap_id}/followers", page) + def render("followers.json", %{user: user, page: page} = opts) do + showing = (opts[:for] && opts[:for] == user) || !user.info.hide_followers + + query = User.get_followers_query(user) + query = from(user in query, select: [:ap_id]) + followers = Repo.all(query) + + total = + if showing do + length(followers) + else + 0 + end + + collection(followers, "#{user.ap_id}/followers", page, showing, total) |> Map.merge(Utils.make_json_ld_header()) end - def render("followers.json", %{user: user}) do - {:ok, followers} = User.get_followers(user) + def render("followers.json", %{user: user} = opts) do + showing = (opts[:for] && opts[:for] == user) || !user.info.hide_followers + + query = User.get_followers_query(user) + query = from(user in query, select: [:ap_id]) + followers = Repo.all(query) + + total = + if showing do + length(followers) + else + 0 + end + %{ "id" => "#{user.ap_id}/followers", "type" => "OrderedCollection", - "totalItems" => length(followers), - "first" => collection(followers, "#{user.ap_id}/followers", 1) + "totalItems" => total, + "first" => + if showing do + collection(followers, "#{user.ap_id}/followers", 1, showing, total) + else + "#{user.ap_id}/followers?page=1" + end } |> Map.merge(Utils.make_json_ld_header()) end def render("outbox.json", %{user: user, max_id: max_qid}) do - # XXX: technically note_count is wrong for this, but it's better than nothing - info = User.user_info(user) - params = %{ - "type" => ["Create", "Announce"], - "actor_id" => user.ap_id, - "whole_db" => true, "limit" => "10" } - if max_qid != nil do - params = Map.put(params, "max_id", max_qid) + params = + if max_qid != nil do + Map.put(params, "max_id", max_qid) + else + params + end + + activities = ActivityPub.fetch_user_activities(user, nil, params) + + {max_id, min_id, collection} = + if length(activities) > 0 do + { + Enum.at(Enum.reverse(activities), 0).id, + Enum.at(activities, 0).id, + Enum.map(activities, fn act -> + {:ok, data} = Transmogrifier.prepare_outgoing(act.data) + data + end) + } + else + { + 0, + 0, + [] + } + end + + iri = "#{user.ap_id}/outbox" + + page = %{ + "id" => "#{iri}?max_id=#{max_id}", + "type" => "OrderedCollectionPage", + "partOf" => iri, + "orderedItems" => collection, + "next" => "#{iri}?max_id=#{min_id}" + } + + if max_qid == nil do + %{ + "id" => iri, + "type" => "OrderedCollection", + "first" => page + } + |> Map.merge(Utils.make_json_ld_header()) + else + page |> Map.merge(Utils.make_json_ld_header()) end + end - activities = ActivityPub.fetch_public_activities(params) - min_id = Enum.at(activities, 0).id + def render("inbox.json", %{user: user, max_id: max_qid}) do + params = %{ + "limit" => "10" + } + + params = + if max_qid != nil do + Map.put(params, "max_id", max_qid) + else + params + end + + activities = ActivityPub.fetch_activities([user.ap_id | user.following], params) - activities = Enum.reverse(activities) + min_id = Enum.at(Enum.reverse(activities), 0).id max_id = Enum.at(activities, 0).id - collection = Enum.map(activities, fn (act) -> - {:ok, data} = Transmogrifier.prepare_outgoing(act.data) - data - end) + collection = + Enum.map(activities, fn act -> + {:ok, data} = Transmogrifier.prepare_outgoing(act.data) + data + end) + + iri = "#{user.ap_id}/inbox" - iri = "#{user.ap_id}/outbox" page = %{ "id" => "#{iri}?max_id=#{max_id}", "type" => "OrderedCollectionPage", "partOf" => iri, - "totalItems" => info.note_count, "orderedItems" => collection, - "next" => "#{iri}?max_id=#{min_id-1}", + "next" => "#{iri}?max_id=#{min_id}" } if max_qid == nil do %{ "id" => iri, "type" => "OrderedCollection", - "totalItems" => info.note_count, "first" => page } |> Map.merge(Utils.make_json_ld_header()) @@ -142,4 +286,38 @@ defmodule Pleroma.Web.ActivityPub.UserView do page |> Map.merge(Utils.make_json_ld_header()) end end + + def collection(collection, iri, page, show_items \\ true, total \\ nil) do + offset = (page - 1) * 10 + items = Enum.slice(collection, offset, 10) + items = Enum.map(items, fn user -> user.ap_id end) + total = total || length(collection) + + map = %{ + "id" => "#{iri}?page=#{page}", + "type" => "OrderedCollectionPage", + "partOf" => iri, + "totalItems" => total, + "orderedItems" => if(show_items, do: items, else: []) + } + + if offset < total do + Map.put(map, "next", "#{iri}?page=#{page + 1}") + else + map + end + end + + defp maybe_make_image(func, key, user) do + if image = func.(user, no_default: true) do + %{ + key => %{ + "type" => "Image", + "url" => image + } + } + else + %{} + end + end end