From 9d0b989521dc181eea2e5912445df1c543457a90 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Fri, 8 Nov 2019 09:23:24 +0300 Subject: [PATCH] add subject to atom feed --- CHANGELOG.md | 1 + config/config.exs | 6 +++ lib/pleroma/web/activity_pub/activity_pub.ex | 1 - lib/pleroma/web/feed/feed_controller.ex | 21 +++++----- lib/pleroma/web/feed/feed_view.ex | 39 +++++++++-------- .../controllers/timeline_controller.ex | 2 - .../web/templates/feed/feed/_activity.xml.eex | 8 ++-- .../web/templates/feed/feed/feed.xml.eex | 2 +- test/web/activity_pub/activity_pub_test.exs | 42 +++++++++---------- test/web/feed/feed_controller_test.exs | 30 +++++++++++-- 10 files changed, 92 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b33d61819..1eb7d14cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Pleroma API: `POST /api/v1/pleroma/conversations/read` to mark all conversations as read - Mastodon API: Add `/api/v1/markers` for managing timeline read markers - Mastodon API: Add the `recipients` parameter to `GET /api/v1/conversations` +- Configuration: `feed` option for user atom feed. ### Fixed diff --git a/config/config.exs b/config/config.exs index 787809b27..7e1fe2a81 100644 --- a/config/config.exs +++ b/config/config.exs @@ -276,6 +276,12 @@ external_user_synchronization: true, extended_nickname_format: false +config :pleroma, :feed, + post_title: %{ + max_length: 100, + omission: "..." + } + config :pleroma, :markup, # XXX - unfortunately, inline images must be enabled by default right now, because # of custom emoji. Issue #275 discusses defanging that somehow. diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 51a9c6169..65dd251f3 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -568,7 +568,6 @@ def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do |> fetch_activities_query(opts) |> restrict_unlisted() |> Pagination.fetch_paginated(opts, pagination) - |> Enum.reverse() end @valid_visibilities ~w[direct unlisted public private] diff --git a/lib/pleroma/web/feed/feed_controller.ex b/lib/pleroma/web/feed/feed_controller.ex index d91ecef9c..d0e23007d 100644 --- a/lib/pleroma/web/feed/feed_controller.ex +++ b/lib/pleroma/web/feed/feed_controller.ex @@ -33,21 +33,22 @@ def feed_redirect(conn, %{"nickname" => nickname}) do def feed(conn, %{"nickname" => nickname} = params) do with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do - query_params = - params - |> Map.take(["max_id"]) - |> Map.put("type", ["Create"]) - |> Map.put("whole_db", true) - |> Map.put("actor_id", user.ap_id) - activities = - query_params + %{ + "type" => ["Create"], + "whole_db" => true, + "actor_id" => user.ap_id + } + |> Map.merge(Map.take(params, ["max_id"])) |> ActivityPub.fetch_public_activities() - |> Enum.reverse() conn |> put_resp_content_type("application/atom+xml") - |> render("feed.xml", user: user, activities: activities) + |> render("feed.xml", + user: user, + activities: activities, + feed_config: Pleroma.Config.get([:feed]) + ) end end diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex index 5eef1e757..bb1332fd3 100644 --- a/lib/pleroma/web/feed/feed_view.ex +++ b/lib/pleroma/web/feed/feed_view.ex @@ -6,12 +6,23 @@ defmodule Pleroma.Web.Feed.FeedView do use Phoenix.HTML use Pleroma.Web, :view + alias Pleroma.Formatter alias Pleroma.Object alias Pleroma.User alias Pleroma.Web.MediaProxy require Pleroma.Constants + def prepare_activity(activity) do + object = activity_object(activity) + + %{ + activity: activity, + data: Map.get(object, :data), + object: object + } + end + def most_recent_update(activities, user) do (List.first(activities) || user).updated_at |> NaiveDateTime.to_iso8601() @@ -23,31 +34,23 @@ def logo(user) do |> MediaProxy.url() end - def last_activity(activities) do - List.last(activities) + def last_activity(activities), do: List.last(activities) + + def activity_object(activity), do: Object.normalize(activity) + + def activity_title(%{data: %{"content" => content}}, opts \\ %{}) do + content + |> Formatter.truncate(opts[:max_length], opts[:omission]) + |> escape() end - def activity_object(activity) do - Object.normalize(activity) - end - - def activity_object_data(activity) do - activity - |> activity_object() - |> Map.get(:data) - end - - def activity_content(activity) do - content = activity_object_data(activity)["content"] - + def activity_content(%{data: %{"content" => content}}) do content |> String.replace(~r/[\n\r]/, "") |> escape() end - def activity_context(activity) do - activity.data["context"] - end + def activity_context(activity), do: activity.data["context"] def attachment_href(attachment) do attachment["url"] diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index f2d2d3ccb..384159336 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -71,7 +71,6 @@ def public(%{assigns: %{user: user}} = conn, params) do |> Map.put("blocking_user", user) |> Map.put("muting_user", user) |> ActivityPub.fetch_public_activities() - |> Enum.reverse() conn |> add_link_headers(activities, %{"local" => local_only}) @@ -110,7 +109,6 @@ def hashtag(%{assigns: %{user: user}} = conn, params) do |> Map.put("tag_all", tag_all) |> Map.put("tag_reject", tag_reject) |> ActivityPub.fetch_public_activities() - |> Enum.reverse() conn |> add_link_headers(activities, %{"local" => local_only}) diff --git a/lib/pleroma/web/templates/feed/feed/_activity.xml.eex b/lib/pleroma/web/templates/feed/feed/_activity.xml.eex index d1f5e903c..514eacaed 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.xml.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.xml.eex @@ -2,11 +2,13 @@ http://activitystrea.ms/schema/1.0/note http://activitystrea.ms/schema/1.0/post <%= @data["id"] %> - <%= "New note by #{@user.nickname}" %> - <%= activity_content(@activity) %> + <%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %> + <%= activity_content(@object) %> <%= @data["published"] %> <%= @data["published"] %> - <%= activity_context(@activity) %> + + <%= activity_context(@activity) %> + <%= if @data["summary"] do %> diff --git a/lib/pleroma/web/templates/feed/feed/feed.xml.eex b/lib/pleroma/web/templates/feed/feed/feed.xml.eex index 45df9dc09..5ae36d345 100644 --- a/lib/pleroma/web/templates/feed/feed/feed.xml.eex +++ b/lib/pleroma/web/templates/feed/feed/feed.xml.eex @@ -19,6 +19,6 @@ <% end %> <%= for activity <- @activities do %> - <%= render @view_module, "_activity.xml", Map.merge(assigns, %{activity: activity, data: activity_object_data(activity)}) %> + <%= render @view_module, "_activity.xml", Map.merge(assigns, prepare_activity(activity)) %> <% end %> diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index f29b8cc74..0d0281faf 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -734,56 +734,54 @@ test "retrieves public activities" do end test "retrieves a maximum of 20 activities" do - activities = ActivityBuilder.insert_list(30) - last_expected = List.last(activities) + ActivityBuilder.insert_list(10) + expected_activities = ActivityBuilder.insert_list(20) activities = ActivityPub.fetch_public_activities() - last = List.last(activities) + assert collect_ids(activities) == collect_ids(expected_activities) assert length(activities) == 20 - assert last == last_expected end test "retrieves ids starting from a since_id" do activities = ActivityBuilder.insert_list(30) - later_activities = ActivityBuilder.insert_list(10) + expected_activities = ActivityBuilder.insert_list(10) since_id = List.last(activities).id - last_expected = List.last(later_activities) activities = ActivityPub.fetch_public_activities(%{"since_id" => since_id}) - last = List.last(activities) + assert collect_ids(activities) == collect_ids(expected_activities) assert length(activities) == 10 - assert last == last_expected end test "retrieves ids up to max_id" do - _first_activities = ActivityBuilder.insert_list(10) - activities = ActivityBuilder.insert_list(20) - later_activities = ActivityBuilder.insert_list(10) - max_id = List.first(later_activities).id - last_expected = List.last(activities) + ActivityBuilder.insert_list(10) + expected_activities = ActivityBuilder.insert_list(20) + + %{id: max_id} = + 10 + |> ActivityBuilder.insert_list() + |> List.first() activities = ActivityPub.fetch_public_activities(%{"max_id" => max_id}) - last = List.last(activities) assert length(activities) == 20 - assert last == last_expected + assert collect_ids(activities) == collect_ids(expected_activities) end test "paginates via offset/limit" do - _first_activities = ActivityBuilder.insert_list(10) - activities = ActivityBuilder.insert_list(10) - _later_activities = ActivityBuilder.insert_list(10) - first_expected = List.first(activities) + _first_part_activities = ActivityBuilder.insert_list(10) + second_part_activities = ActivityBuilder.insert_list(10) + + later_activities = ActivityBuilder.insert_list(10) activities = ActivityPub.fetch_public_activities(%{"page" => "2", "page_size" => "20"}, :offset) - first = List.first(activities) - assert length(activities) == 20 - assert first == first_expected + + assert collect_ids(activities) == + collect_ids(second_part_activities) ++ collect_ids(later_activities) end test "doesn't return reblogs for users for whom reblogs have been muted" do diff --git a/test/web/feed/feed_controller_test.exs b/test/web/feed/feed_controller_test.exs index 1f44eae20..6f61acf43 100644 --- a/test/web/feed/feed_controller_test.exs +++ b/test/web/feed/feed_controller_test.exs @@ -6,16 +6,25 @@ defmodule Pleroma.Web.Feed.FeedControllerTest do use Pleroma.Web.ConnCase import Pleroma.Factory + import SweetXml alias Pleroma.Object alias Pleroma.User + clear_config([:feed]) + test "gets a feed", %{conn: conn} do + Pleroma.Config.put( + [:feed, :post_title], + %{max_length: 10, omission: "..."} + ) + activity = insert(:note_activity) note = insert(:note, data: %{ + "content" => "This is :moominmamma: note ", "attachment" => [ %{ "url" => [%{"mediaType" => "image/png", "href" => "https://pleroma.gov/image.png"}] @@ -26,15 +35,30 @@ test "gets a feed", %{conn: conn} do ) note_activity = insert(:note_activity, note: note) - object = Object.normalize(note_activity) user = User.get_cached_by_ap_id(note_activity.data["actor"]) - conn = + note2 = + insert(:note, + user: user, + data: %{"content" => "42 This is :moominmamma: note ", "inReplyTo" => activity.data["id"]} + ) + + _note_activity2 = insert(:note_activity, note: note2) + object = Object.normalize(note_activity) + + resp = conn |> put_req_header("content-type", "application/atom+xml") |> get("/users/#{user.nickname}/feed.atom") + |> response(200) - assert response(conn, 200) =~ object.data["content"] + activity_titles = + resp + |> SweetXml.parse() + |> SweetXml.xpath(~x"//entry/title/text()"l) + + assert activity_titles == ['42 This...', 'This is...'] + assert resp =~ object.data["content"] end test "returns 404 for a missing feed", %{conn: conn} do