logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma
commit: fa0392e49cd46e4d2845840232a4b56e52729369
parent: c445c9e1255c978c9c0ace0828c4dcdd36e777b9
Author: kaniini <nenolod@gmail.com>
Date:   Mon, 14 Jan 2019 07:27:05 +0000

Merge branch 'activitypub-likes' into 'develop'

Activitypub c2s likes

See merge request pleroma/pleroma!658

Diffstat:

Mlib/pleroma/web/activity_pub/activity_pub_controller.ex39+++++++++++++++++++++++++++++++++++++++
Mlib/pleroma/web/activity_pub/transmogrifier.ex20++++++++++++++++++--
Mlib/pleroma/web/activity_pub/utils.ex21+++++++++++++++++++++
Mlib/pleroma/web/activity_pub/views/object_view.ex34++++++++++++++++++++++++++++++++++
Mlib/pleroma/web/router.ex1+
Mtest/support/factory.ex25+++++++++++++++++++++++++
Mtest/web/activity_pub/activity_pub_controller_test.exs40++++++++++++++++++++++++++++++++++++++++
Mtest/web/activity_pub/transmogrifier_test.exs23++++++++++++++++++++++-
8 files changed, 200 insertions(+), 3 deletions(-)

diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -54,6 +54,36 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do end end + def object_likes(conn, %{"uuid" => uuid, "page" => page}) do + with ap_id <- o_status_url(conn, :object, uuid), + %Object{} = object <- Object.get_cached_by_ap_id(ap_id), + {_, true} <- {:public?, ActivityPub.is_public?(object)}, + likes <- Utils.get_object_likes(object) do + {page, _} = Integer.parse(page) + + conn + |> put_resp_header("content-type", "application/activity+json") + |> json(ObjectView.render("likes.json", ap_id, likes, page)) + else + {:public?, false} -> + {:error, :not_found} + end + end + + def object_likes(conn, %{"uuid" => uuid}) do + with ap_id <- o_status_url(conn, :object, uuid), + %Object{} = object <- Object.get_cached_by_ap_id(ap_id), + {_, true} <- {:public?, ActivityPub.is_public?(object)}, + likes <- Utils.get_object_likes(object) do + conn + |> put_resp_header("content-type", "application/activity+json") + |> json(ObjectView.render("likes.json", ap_id, likes)) + else + {:public?, false} -> + {:error, :not_found} + end + end + def activity(conn, %{"uuid" => uuid}) do with ap_id <- o_status_url(conn, :activity, uuid), %Activity{} = activity <- Activity.normalize(ap_id), @@ -204,6 +234,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do end end + def handle_user_activity(user, %{"type" => "Like"} = params) do + with %Object{} = object <- Object.normalize(params["object"]), + {:ok, activity, _object} <- ActivityPub.like(user, object) do + {:ok, activity} + else + _ -> {:error, "Can't like object"} + end + end + def handle_user_activity(_, _) do {:error, "Unhandled activity type"} end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -629,6 +629,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> add_mention_tags |> add_emoji_tags |> add_attributed_to + |> add_likes |> prepare_attachments |> set_conversation |> set_reply_to_uri @@ -641,7 +642,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do # internal -> Mastodon # """ - def prepare_outgoing(%{"type" => "Create", "object" => %{"type" => "Note"} = object} = data) do + def prepare_outgoing(%{"type" => "Create", "object" => object} = data) do object = object |> prepare_object @@ -788,6 +789,22 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> Map.put("attributedTo", attributedTo) end + def add_likes(%{"id" => id, "like_count" => likes} = object) do + likes = %{ + "id" => "#{id}/likes", + "first" => "#{id}/likes?page=1", + "type" => "OrderedCollection", + "totalItems" => likes + } + + object + |> Map.put("likes", likes) + end + + def add_likes(object) do + object + end + def prepare_attachments(object) do attachments = (object["attachment"] || []) @@ -803,7 +820,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do defp strip_internal_fields(object) do object |> Map.drop([ - "likes", "like_count", "announcements", "announcement_count", diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex @@ -231,6 +231,27 @@ defmodule Pleroma.Web.ActivityPub.Utils do Repo.one(query) end + @doc """ + Returns like activities targeting an object + """ + def get_object_likes(%{data: %{"id" => id}}) do + query = + from( + activity in Activity, + # this is to use the index + where: + fragment( + "coalesce((?)->'object'->>'id', (?)->>'object') = ?", + activity.data, + activity.data, + ^id + ), + where: fragment("(?)->>'type' = 'Like'", activity.data) + ) + + Repo.all(query) + end + def make_like_data(%User{ap_id: ap_id} = actor, %{data: %{"id" => id}} = object, activity_id) do data = %{ "type" => "Like", diff --git a/lib/pleroma/web/activity_pub/views/object_view.ex b/lib/pleroma/web/activity_pub/views/object_view.ex @@ -35,4 +35,38 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do Map.merge(base, additional) end + + def render("likes.json", ap_id, likes, page) do + collection(likes, "#{ap_id}/likes", page) + |> Map.merge(Pleroma.Web.ActivityPub.Utils.make_json_ld_header()) + end + + def render("likes.json", ap_id, likes) do + %{ + "id" => "#{ap_id}/likes", + "type" => "OrderedCollection", + "totalItems" => length(likes), + "first" => collection(likes, "#{ap_id}/followers", 1) + } + |> Map.merge(Pleroma.Web.ActivityPub.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 object -> Transmogrifier.prepare_object(object.data) end) + total = length(collection) + + map = %{ + "id" => "#{iri}?page=#{page}", + "type" => "OrderedCollectionPage", + "partOf" => iri, + "totalItems" => total, + "orderedItems" => items + } + + if offset < total do + Map.put(map, "next", "#{iri}?page=#{page + 1}") + end + end end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex @@ -421,6 +421,7 @@ defmodule Pleroma.Web.Router do get("/users/:nickname/followers", ActivityPubController, :followers) get("/users/:nickname/following", ActivityPubController, :following) get("/users/:nickname/outbox", ActivityPubController, :outbox) + get("/objects/:uuid/likes", ActivityPubController, :object_likes) end pipeline :activitypub_client do diff --git a/test/support/factory.ex b/test/support/factory.ex @@ -57,6 +57,11 @@ defmodule Pleroma.Factory do %Pleroma.Object{data: Map.merge(data, %{"to" => [user2.ap_id]})} end + def article_factory do + note_factory() + |> Map.put("type", "Article") + end + def tombstone_factory do data = %{ "type" => "Tombstone", @@ -110,6 +115,26 @@ defmodule Pleroma.Factory do } end + def article_activity_factory do + article = insert(:article) + + data = %{ + "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), + "type" => "Create", + "actor" => article.data["actor"], + "to" => article.data["to"], + "object" => article.data, + "published" => DateTime.utc_now() |> DateTime.to_iso8601(), + "context" => article.data["context"] + } + + %Pleroma.Activity{ + data: data, + actor: data["actor"], + recipients: data["to"] + } + end + def announce_activity_factory do note_activity = insert(:note_activity) user = insert(:user) diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs @@ -89,6 +89,21 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do end end + describe "/object/:uuid/likes" do + test "it returns the like activities in a collection", %{conn: conn} do + like = insert(:like_activity) + uuid = String.split(like.data["object"], "/") |> List.last() + + result = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/objects/#{uuid}/likes") + |> json_response(200) + + assert List.first(result["first"]["orderedItems"])["id"] == like.data["id"] + end + end + describe "/activities/:uuid" do test "it returns a json representation of the activity", %{conn: conn} do activity = insert(:note_activity) @@ -292,6 +307,31 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert json_response(conn, 400) end + + test "it increases like count when receiving a like action", %{conn: conn} do + note_activity = insert(:note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + data = %{ + type: "Like", + object: %{ + id: note_activity.data["object"]["id"] + } + } + + conn = + conn + |> assign(:user, user) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/outbox", data) + + result = json_response(conn, 201) + assert Activity.get_by_ap_id(result["id"]) + + object = Object.get_by_ap_id(note_activity.data["object"]["id"]) + assert object + assert object.data["like_count"] == 1 + end end describe "/users/:nickname/followers" do diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs @@ -829,12 +829,33 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert length(modified["object"]["tag"]) == 2 assert is_nil(modified["object"]["emoji"]) - assert is_nil(modified["object"]["likes"]) assert is_nil(modified["object"]["like_count"]) assert is_nil(modified["object"]["announcements"]) assert is_nil(modified["object"]["announcement_count"]) assert is_nil(modified["object"]["context_id"]) end + + test "it strips internal fields of article" do + activity = insert(:article_activity) + + {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + + assert length(modified["object"]["tag"]) == 2 + + assert is_nil(modified["object"]["emoji"]) + assert is_nil(modified["object"]["like_count"]) + assert is_nil(modified["object"]["announcements"]) + assert is_nil(modified["object"]["announcement_count"]) + assert is_nil(modified["object"]["context_id"]) + end + + test "it adds like collection to object" do + activity = insert(:note_activity) + {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + + assert modified["object"]["likes"]["type"] == "OrderedCollection" + assert modified["object"]["likes"]["totalItems"] == 0 + end end describe "user upgrade" do