Extract RSS Feed functionality from OStatus
authorSergey Suprunenko <suprunenko.s@gmail.com>
Mon, 7 Oct 2019 12:20:41 +0000 (12:20 +0000)
committerkaniini <ariadne@dereferenced.org>
Mon, 7 Oct 2019 12:20:41 +0000 (12:20 +0000)
17 files changed:
CHANGELOG.md
config/config.exs
docs/configuration/cheatsheet.md
lib/pleroma/emails/admin_email.ex
lib/pleroma/web/feed/feed_controller.ex [new file with mode: 0644]
lib/pleroma/web/feed/feed_view.ex [new file with mode: 0644]
lib/pleroma/web/metadata/feed.ex [new file with mode: 0644]
lib/pleroma/web/ostatus/ostatus_controller.ex
lib/pleroma/web/router.ex
lib/pleroma/web/templates/feed/feed/_activity.xml.eex [new file with mode: 0644]
lib/pleroma/web/templates/feed/feed/_author.xml.eex [new file with mode: 0644]
lib/pleroma/web/templates/feed/feed/feed.xml.eex [new file with mode: 0644]
test/emails/admin_email_test.exs
test/user_test.exs
test/web/feed/feed_controller_test.exs [new file with mode: 0644]
test/web/metadata/feed_test.exs [new file with mode: 0644]
test/web/ostatus/ostatus_controller_test.exs

index f480a3f72c85c1e1b03850024deb818986c5faad..f193ec1ff30b95e2039f1473cd09effcd37e6942 100644 (file)
@@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Mastodon API: Add `pleroma.unread_conversation_count` to the Account entity
 - OAuth: support for hierarchical permissions / [Mastodon 2.4.3 OAuth permissions](https://docs.joinmastodon.org/api/permissions/)
 - Authentication: Added rate limit for password-authorized actions / login existence checks
+- Metadata Link: Atom syndication Feed
 
 ### Changed
 - **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
@@ -25,6 +26,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Mastodon API: Return `pleroma.direct_conversation_id` when creating a direct message (`POST /api/v1/statuses`)
 - Admin API: Return link alongside with token on password reset
 - MRF (Simple Policy): Also use `:accept`/`:reject` on the actors rather than only their activities
+- OStatus: Extract RSS functionality
 
 ### Fixed
 - Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`)
index cf94f1a19062733d5d9a5b2414194e5131042d11..f4d92102f70743f6f8d367eb977d11d0a073c7f8 100644 (file)
@@ -409,7 +409,8 @@ config :pleroma, Pleroma.Web.Metadata,
   providers: [
     Pleroma.Web.Metadata.Providers.OpenGraph,
     Pleroma.Web.Metadata.Providers.TwitterCard,
-    Pleroma.Web.Metadata.Providers.RelMe
+    Pleroma.Web.Metadata.Providers.RelMe,
+    Pleroma.Web.Metadata.Providers.Feed
   ],
   unfurl_nsfw: false
 
index b86799ecc87e4844606b5db76736d23e058df0ab..8d85276edd85e8dfe17b092ac1cefedee4677836 100644 (file)
@@ -475,6 +475,7 @@ config :pleroma, :workers,
   * Pleroma.Web.Metadata.Providers.OpenGraph
   * Pleroma.Web.Metadata.Providers.TwitterCard
   * Pleroma.Web.Metadata.Providers.RelMe - add links from user bio with rel=me into the `<header>` as `<link rel=me>`
+  * Pleroma.Web.Metadata.Providers.Feed - add a link to a user's Atom feed into the `<header>` as `<link rel=alternate>`
 * `unfurl_nsfw`: If set to `true` nsfw attachments will be shown in previews
 
 ## :rich_media
index 5a0903c13143754a44fd49fbfad7c170cd950da8..b15e4041bd4295d17d3a6a5124748ff87c60778a 100644 (file)
@@ -17,7 +17,7 @@ defmodule Pleroma.Emails.AdminEmail do
   end
 
   defp user_url(user) do
-    Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, user.id)
+    Helpers.feed_url(Pleroma.Web.Endpoint, :feed_redirect, user.id)
   end
 
   def report(to, reporter, account, statuses, comment) do
diff --git a/lib/pleroma/web/feed/feed_controller.ex b/lib/pleroma/web/feed/feed_controller.ex
new file mode 100644 (file)
index 0000000..d91ecef
--- /dev/null
@@ -0,0 +1,63 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Feed.FeedController do
+  use Pleroma.Web, :controller
+
+  alias Fallback.RedirectController
+  alias Pleroma.User
+  alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.ActivityPub.ActivityPubController
+
+  plug(Pleroma.Plugs.SetFormatPlug when action in [:feed_redirect])
+
+  action_fallback(:errors)
+
+  def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do
+    with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do
+      RedirectController.redirector_with_meta(conn, %{user: user})
+    end
+  end
+
+  def feed_redirect(%{assigns: %{format: format}} = conn, _params)
+      when format in ["json", "activity+json"] do
+    ActivityPubController.call(conn, :user)
+  end
+
+  def feed_redirect(conn, %{"nickname" => nickname}) do
+    with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
+      redirect(conn, external: "#{feed_url(conn, :feed, user.nickname)}.atom")
+    end
+  end
+
+  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
+        |> ActivityPub.fetch_public_activities()
+        |> Enum.reverse()
+
+      conn
+      |> put_resp_content_type("application/atom+xml")
+      |> render("feed.xml", user: user, activities: activities)
+    end
+  end
+
+  def errors(conn, {:error, :not_found}) do
+    render_error(conn, :not_found, "Not found")
+  end
+
+  def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found})
+
+  def errors(conn, _) do
+    render_error(conn, :internal_server_error, "Something went wrong")
+  end
+end
diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex
new file mode 100644 (file)
index 0000000..5eef1e7
--- /dev/null
@@ -0,0 +1,77 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Feed.FeedView do
+  use Phoenix.HTML
+  use Pleroma.Web, :view
+
+  alias Pleroma.Object
+  alias Pleroma.User
+  alias Pleroma.Web.MediaProxy
+
+  require Pleroma.Constants
+
+  def most_recent_update(activities, user) do
+    (List.first(activities) || user).updated_at
+    |> NaiveDateTime.to_iso8601()
+  end
+
+  def logo(user) do
+    user
+    |> User.avatar_url()
+    |> MediaProxy.url()
+  end
+
+  def last_activity(activities) do
+    List.last(activities)
+  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"]
+
+    content
+    |> String.replace(~r/[\n\r]/, "")
+    |> escape()
+  end
+
+  def activity_context(activity) do
+    activity.data["context"]
+  end
+
+  def attachment_href(attachment) do
+    attachment["url"]
+    |> hd()
+    |> Map.get("href")
+  end
+
+  def attachment_type(attachment) do
+    attachment["url"]
+    |> hd()
+    |> Map.get("mediaType")
+  end
+
+  def get_href(id) do
+    with %Object{data: %{"external_url" => external_url}} <- Object.get_cached_by_ap_id(id) do
+      external_url
+    else
+      _e -> id
+    end
+  end
+
+  def escape(html) do
+    html
+    |> html_escape()
+    |> safe_to_string()
+  end
+end
diff --git a/lib/pleroma/web/metadata/feed.ex b/lib/pleroma/web/metadata/feed.ex
new file mode 100644 (file)
index 0000000..8043e6c
--- /dev/null
@@ -0,0 +1,23 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Metadata.Providers.Feed do
+  alias Pleroma.Web.Endpoint
+  alias Pleroma.Web.Metadata.Providers.Provider
+  alias Pleroma.Web.Router.Helpers
+
+  @behaviour Provider
+
+  @impl Provider
+  def build_tags(%{user: user}) do
+    [
+      {:link,
+       [
+         rel: "alternate",
+         type: "application/atom+xml",
+         href: Helpers.feed_path(Endpoint, :feed, user.nickname) <> ".atom"
+       ], []}
+    ]
+  end
+end
index 8f325b28eb5bcce385af124764f0c0513592624a..20f2d9ddc58ef507ccec9384569cb9ec7cc56008 100644 (file)
@@ -9,16 +9,13 @@ defmodule Pleroma.Web.OStatus.OStatusController do
   alias Pleroma.Activity
   alias Pleroma.Object
   alias Pleroma.User
-  alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.ActivityPubController
   alias Pleroma.Web.ActivityPub.ObjectView
   alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.Endpoint
   alias Pleroma.Web.Federator
   alias Pleroma.Web.Metadata.PlayerView
-  alias Pleroma.Web.OStatus
   alias Pleroma.Web.OStatus.ActivityRepresenter
-  alias Pleroma.Web.OStatus.FeedRepresenter
   alias Pleroma.Web.Router
   alias Pleroma.Web.XML
 
@@ -31,49 +28,11 @@ defmodule Pleroma.Web.OStatus.OStatusController do
 
   plug(
     Pleroma.Plugs.SetFormatPlug
-    when action in [:feed_redirect, :object, :activity, :notice]
+    when action in [:object, :activity, :notice]
   )
 
   action_fallback(:errors)
 
-  def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do
-    with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do
-      RedirectController.redirector_with_meta(conn, %{user: user})
-    end
-  end
-
-  def feed_redirect(%{assigns: %{format: format}} = conn, _params)
-      when format in ["json", "activity+json"] do
-    ActivityPubController.call(conn, :user)
-  end
-
-  def feed_redirect(conn, %{"nickname" => nickname}) do
-    with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
-      redirect(conn, external: OStatus.feed_path(user))
-    end
-  end
-
-  def feed(conn, %{"nickname" => nickname} = params) do
-    with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
-      activities =
-        params
-        |> Map.take(["max_id"])
-        |> Map.merge(%{"whole_db" => true, "actor_id" => user.ap_id})
-        |> ActivityPub.fetch_public_activities()
-        |> Enum.reverse()
-
-      response =
-        user
-        |> FeedRepresenter.to_simple_form(activities, [user])
-        |> :xmerl.export_simple(:xmerl_xml)
-        |> to_string
-
-      conn
-      |> put_resp_content_type("application/atom+xml")
-      |> send_resp(200, response)
-    end
-  end
-
   defp decode_or_retry(body) do
     with {:ok, magic_key} <- Pleroma.Web.Salmon.fetch_magic_key(body),
          {:ok, doc} <- Pleroma.Web.Salmon.decode_and_validate(magic_key, body) do
index 675f485b24eaad2eb9b73f37f3746738dec01d59..ae799b8ac36650112f23e7756d0cf475754557a0 100644 (file)
@@ -495,8 +495,9 @@ defmodule Pleroma.Web.Router do
     get("/activities/:uuid", OStatus.OStatusController, :activity)
     get("/notice/:id", OStatus.OStatusController, :notice)
     get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player)
-    get("/users/:nickname/feed", OStatus.OStatusController, :feed)
-    get("/users/:nickname", OStatus.OStatusController, :feed_redirect)
+
+    get("/users/:nickname/feed", Feed.FeedController, :feed)
+    get("/users/:nickname", Feed.FeedController, :feed_redirect)
 
     post("/users/:nickname/salmon", OStatus.OStatusController, :salmon_incoming)
     post("/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request)
diff --git a/lib/pleroma/web/templates/feed/feed/_activity.xml.eex b/lib/pleroma/web/templates/feed/feed/_activity.xml.eex
new file mode 100644 (file)
index 0000000..d1f5e90
--- /dev/null
@@ -0,0 +1,48 @@
+<entry>
+  <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
+  <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+  <id><%= @data["id"] %></id>
+  <title><%= "New note by #{@user.nickname}" %></title>
+  <content type="html"><%= activity_content(@activity) %></content>
+  <published><%= @data["published"] %></published>
+  <updated><%= @data["published"] %></updated>
+  <ostatus:conversation ref="<%= activity_context(@activity) %>"><%= activity_context(@activity) %></ostatus:conversation>
+  <link ref="<%= activity_context(@activity) %>" rel="ostatus:conversation"/>
+
+  <%= if @data["summary"] do %>
+    <summary><%= @data["summary"] %></summary>
+  <% end %>
+
+  <%= if @activity.local do %>
+    <link type="application/atom+xml" href='<%= @data["id"] %>' rel="self"/>
+    <link type="text/html" href='<%= @data["id"] %>' rel="alternate"/>
+  <% else %>
+    <link type="text/html" href='<%= @data["external_url"] %>' rel="alternate"/>
+  <% end %>
+
+  <%= for tag <- @data["tag"] || [] do %>
+    <category term="<%= tag %>"></category>
+  <% end %>
+
+  <%= for attachment <- @data["attachment"] || [] do %>
+    <link rel="enclosure" href="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>"/>
+  <% end %>
+
+  <%= if @data["inReplyTo"] do %>
+    <thr:in-reply-to ref='<%= @data["inReplyTo"] %>' href='<%= get_href(@data["inReplyTo"]) %>'/>
+  <% end %>
+
+  <%= for id <- @activity.recipients do %>
+    <%= if id == Pleroma.Constants.as_public() do %>
+      <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+    <% else %>
+      <%= unless Regex.match?(~r/^#{Pleroma.Web.base_url()}.+followers$/, id) do %>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="<%= id %>"/>
+      <% end %>
+    <% end %>
+  <% end %>
+
+  <%= for {emoji, file} <- @data["emoji"] || %{} do %>
+    <link name="<%= emoji %>" rel="emoji" href="<%= file %>"/>
+  <% end %>
+</entry>
diff --git a/lib/pleroma/web/templates/feed/feed/_author.xml.eex b/lib/pleroma/web/templates/feed/feed/_author.xml.eex
new file mode 100644 (file)
index 0000000..25cbffa
--- /dev/null
@@ -0,0 +1,17 @@
+<author>
+  <id><%= @user.ap_id %></id>
+  <activity:object>http://activitystrea.ms/schema/1.0/person</activity:object>
+  <uri><%= @user.ap_id %></uri>
+  <poco:preferredUsername><%= @user.nickname %></poco:preferredUsername>
+  <poco:displayName><%= @user.name %></poco:displayName>
+  <poco:note><%= escape(@user.bio) %></poco:note>
+  <summary><%= escape(@user.bio) %></summary>
+  <name><%= @user.nickname %></name>
+  <link rel="avatar" href="<%= User.avatar_url(@user) %>"/>
+  <%= if User.banner_url(@user) do %>
+    <link rel="header" href="<%= User.banner_url(@user) %>"/>
+  <% end %>
+  <%= if @user.local do %>
+    <ap_enabled>true</ap_enabled>
+  <% end %>
+</author>
diff --git a/lib/pleroma/web/templates/feed/feed/feed.xml.eex b/lib/pleroma/web/templates/feed/feed/feed.xml.eex
new file mode 100644 (file)
index 0000000..fbfdc46
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feed
+  xmlns="http://www.w3.org/2005/Atom"
+  xmlns:thr="http://purl.org/syndication/thread/1.0"
+  xmlns:activity="http://activitystrea.ms/spec/1.0/"
+  xmlns:poco="http://portablecontacts.net/spec/1.0"
+  xmlns:ostatus="http://ostatus.org/schema/1.0">
+
+  <id><%= feed_url(@conn, :feed, @user.nickname) <> ".atom" %></id>
+  <title><%= @user.nickname <> "'s timeline" %></title>
+  <updated><%= most_recent_update(@activities, @user) %></updated>
+  <logo><%= logo(@user) %></logo>
+  <link rel="hub" href="<%= websub_url(@conn, :websub_subscription_request, @user.nickname) %>"/>
+  <link rel="salmon" href="<%= o_status_url(@conn, :salmon_incoming, @user.nickname) %>"/>
+  <link rel="self" href="<%= '#{feed_url(@conn, :feed, @user.nickname)}.atom' %>" type="application/atom+xml"/>
+
+  <%= render @view_module, "_author.xml", assigns %>
+
+  <%= if last_activity(@activities) do %>
+    <link rel="next" href="<%= '#{feed_url(@conn, :feed, @user.nickname)}.atom?max_id=#{last_activity(@activities).id}' %>" type="application/atom+xml"/>
+  <% end %>
+
+  <%= for activity <- @activities do %>
+    <%= render @view_module, "_activity.xml", Map.merge(assigns, %{activity: activity, data: activity_object_data(activity)}) %>
+  <% end %>
+</feed>
index 02c277a339803b75e533cf017d7e9d88a9037566..ad89f9213b10d54ecb70920eed7a4a7fcf9dfa75 100644 (file)
@@ -19,8 +19,8 @@ defmodule Pleroma.Emails.AdminEmailTest do
       AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment")
 
     status_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, "12")
-    reporter_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.id)
-    account_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, account.id)
+    reporter_url = Helpers.feed_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.id)
+    account_url = Helpers.feed_url(Pleroma.Web.Endpoint, :feed_redirect, account.id)
 
     assert res.to == [{to_user.name, to_user.email}]
     assert res.from == {config[:name], config[:notify_email]}
index ae21286e407260f28b8b625d2b00b25f6bf6d32d..019e7b400c32f4b2e5957fb303caa96f956c2eda 100644 (file)
@@ -515,7 +515,7 @@ defmodule Pleroma.UserTest do
     user = insert(:user)
 
     assert User.ap_id(user) ==
-             Pleroma.Web.Router.Helpers.o_status_url(
+             Pleroma.Web.Router.Helpers.feed_url(
                Pleroma.Web.Endpoint,
                :feed_redirect,
                user.nickname
@@ -526,7 +526,7 @@ defmodule Pleroma.UserTest do
     user = insert(:user)
 
     assert User.ap_followers(user) ==
-             Pleroma.Web.Router.Helpers.o_status_url(
+             Pleroma.Web.Router.Helpers.feed_url(
                Pleroma.Web.Endpoint,
                :feed_redirect,
                user.nickname
diff --git a/test/web/feed/feed_controller_test.exs b/test/web/feed/feed_controller_test.exs
new file mode 100644 (file)
index 0000000..1f44eae
--- /dev/null
@@ -0,0 +1,227 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Feed.FeedControllerTest do
+  use Pleroma.Web.ConnCase
+
+  import Pleroma.Factory
+
+  alias Pleroma.Object
+  alias Pleroma.User
+
+  test "gets a feed", %{conn: conn} do
+    activity = insert(:note_activity)
+
+    note =
+      insert(:note,
+        data: %{
+          "attachment" => [
+            %{
+              "url" => [%{"mediaType" => "image/png", "href" => "https://pleroma.gov/image.png"}]
+            }
+          ],
+          "inReplyTo" => activity.data["id"]
+        }
+      )
+
+    note_activity = insert(:note_activity, note: note)
+    object = Object.normalize(note_activity)
+    user = User.get_cached_by_ap_id(note_activity.data["actor"])
+
+    conn =
+      conn
+      |> put_req_header("content-type", "application/atom+xml")
+      |> get("/users/#{user.nickname}/feed.atom")
+
+    assert response(conn, 200) =~ object.data["content"]
+  end
+
+  test "returns 404 for a missing feed", %{conn: conn} do
+    conn =
+      conn
+      |> put_req_header("content-type", "application/atom+xml")
+      |> get("/users/nonexisting/feed.atom")
+
+    assert response(conn, 404)
+  end
+
+  describe "feed_redirect" do
+    test "undefined format. it redirects to feed", %{conn: conn} do
+      note_activity = insert(:note_activity)
+      user = User.get_cached_by_ap_id(note_activity.data["actor"])
+
+      response =
+        conn
+        |> put_req_header("accept", "application/xml")
+        |> get("/users/#{user.nickname}")
+        |> response(302)
+
+      assert response ==
+               "<html><body>You are being <a href=\"#{Pleroma.Web.base_url()}/users/#{
+                 user.nickname
+               }/feed.atom\">redirected</a>.</body></html>"
+    end
+
+    test "undefined format. it returns error when user not found", %{conn: conn} do
+      response =
+        conn
+        |> put_req_header("accept", "application/xml")
+        |> get("/users/jimm")
+        |> response(404)
+
+      assert response == ~S({"error":"Not found"})
+    end
+
+    test "activity+json format. it redirects on actual feed of user", %{conn: conn} do
+      note_activity = insert(:note_activity)
+      user = User.get_cached_by_ap_id(note_activity.data["actor"])
+
+      response =
+        conn
+        |> put_req_header("accept", "application/activity+json")
+        |> get("/users/#{user.nickname}")
+        |> json_response(200)
+
+      assert response["endpoints"] == %{
+               "oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize",
+               "oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps",
+               "oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token",
+               "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox",
+               "uploadMedia" => "#{Pleroma.Web.base_url()}/api/ap/upload_media"
+             }
+
+      assert response["@context"] == [
+               "https://www.w3.org/ns/activitystreams",
+               "http://localhost:4001/schemas/litepub-0.1.jsonld",
+               %{"@language" => "und"}
+             ]
+
+      assert Map.take(response, [
+               "followers",
+               "following",
+               "id",
+               "inbox",
+               "manuallyApprovesFollowers",
+               "name",
+               "outbox",
+               "preferredUsername",
+               "summary",
+               "tag",
+               "type",
+               "url"
+             ]) == %{
+               "followers" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/followers",
+               "following" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/following",
+               "id" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}",
+               "inbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/inbox",
+               "manuallyApprovesFollowers" => false,
+               "name" => user.name,
+               "outbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/outbox",
+               "preferredUsername" => user.nickname,
+               "summary" => user.bio,
+               "tag" => [],
+               "type" => "Person",
+               "url" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}"
+             }
+    end
+
+    test "activity+json format. it returns error whe use not found", %{conn: conn} do
+      response =
+        conn
+        |> put_req_header("accept", "application/activity+json")
+        |> get("/users/jimm")
+        |> json_response(404)
+
+      assert response == "Not found"
+    end
+
+    test "json format. it redirects on actual feed of user", %{conn: conn} do
+      note_activity = insert(:note_activity)
+      user = User.get_cached_by_ap_id(note_activity.data["actor"])
+
+      response =
+        conn
+        |> put_req_header("accept", "application/json")
+        |> get("/users/#{user.nickname}")
+        |> json_response(200)
+
+      assert response["endpoints"] == %{
+               "oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize",
+               "oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps",
+               "oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token",
+               "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox",
+               "uploadMedia" => "#{Pleroma.Web.base_url()}/api/ap/upload_media"
+             }
+
+      assert response["@context"] == [
+               "https://www.w3.org/ns/activitystreams",
+               "http://localhost:4001/schemas/litepub-0.1.jsonld",
+               %{"@language" => "und"}
+             ]
+
+      assert Map.take(response, [
+               "followers",
+               "following",
+               "id",
+               "inbox",
+               "manuallyApprovesFollowers",
+               "name",
+               "outbox",
+               "preferredUsername",
+               "summary",
+               "tag",
+               "type",
+               "url"
+             ]) == %{
+               "followers" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/followers",
+               "following" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/following",
+               "id" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}",
+               "inbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/inbox",
+               "manuallyApprovesFollowers" => false,
+               "name" => user.name,
+               "outbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/outbox",
+               "preferredUsername" => user.nickname,
+               "summary" => user.bio,
+               "tag" => [],
+               "type" => "Person",
+               "url" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}"
+             }
+    end
+
+    test "json format. it returns error whe use not found", %{conn: conn} do
+      response =
+        conn
+        |> put_req_header("accept", "application/json")
+        |> get("/users/jimm")
+        |> json_response(404)
+
+      assert response == "Not found"
+    end
+
+    test "html format. it redirects on actual feed of user", %{conn: conn} do
+      note_activity = insert(:note_activity)
+      user = User.get_cached_by_ap_id(note_activity.data["actor"])
+
+      response =
+        conn
+        |> get("/users/#{user.nickname}")
+        |> response(200)
+
+      assert response ==
+               Fallback.RedirectController.redirector_with_meta(
+                 conn,
+                 %{user: user}
+               ).resp_body
+    end
+
+    test "html format. it returns error when user not found", %{conn: conn} do
+      response =
+        conn
+        |> get("/users/jimm")
+        |> json_response(404)
+
+      assert response == %{"error" => "Not found"}
+    end
+  end
+end
diff --git a/test/web/metadata/feed_test.exs b/test/web/metadata/feed_test.exs
new file mode 100644 (file)
index 0000000..50e9ce5
--- /dev/null
@@ -0,0 +1,18 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Metadata.Providers.FeedTest do
+  use Pleroma.DataCase
+  import Pleroma.Factory
+  alias Pleroma.Web.Metadata.Providers.Feed
+
+  test "it renders a link to user's atom feed" do
+    user = insert(:user, nickname: "lain")
+
+    assert Feed.build_tags(%{user: user}) == [
+             {:link,
+              [rel: "alternate", type: "application/atom+xml", href: "/users/lain/feed.atom"], []}
+           ]
+  end
+end
index f06023dff88e31e3fe515f0bab5e8bcecaf8ce4b..b1af918d8792c8ce76076948c0e3ddac50e20287 100644 (file)
@@ -72,28 +72,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
     end
   end
 
-  test "gets a feed", %{conn: conn} do
-    note_activity = insert(:note_activity)
-    object = Object.normalize(note_activity)
-    user = User.get_cached_by_ap_id(note_activity.data["actor"])
-
-    conn =
-      conn
-      |> put_req_header("content-type", "application/atom+xml")
-      |> get("/users/#{user.nickname}/feed.atom")
-
-    assert response(conn, 200) =~ object.data["content"]
-  end
-
-  test "returns 404 for a missing feed", %{conn: conn} do
-    conn =
-      conn
-      |> put_req_header("content-type", "application/atom+xml")
-      |> get("/users/nonexisting/feed.atom")
-
-    assert response(conn, 404)
-  end
-
   describe "GET object/2" do
     test "gets an object", %{conn: conn} do
       note_activity = insert(:note_activity)
@@ -355,185 +333,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
     end
   end
 
-  describe "feed_redirect" do
-    test "undefined format. it redirects to feed", %{conn: conn} do
-      note_activity = insert(:note_activity)
-      user = User.get_cached_by_ap_id(note_activity.data["actor"])
-
-      response =
-        conn
-        |> put_req_header("accept", "application/xml")
-        |> get("/users/#{user.nickname}")
-        |> response(302)
-
-      assert response ==
-               "<html><body>You are being <a href=\"#{Pleroma.Web.base_url()}/users/#{
-                 user.nickname
-               }/feed.atom\">redirected</a>.</body></html>"
-    end
-
-    test "undefined format. it returns error when user not found", %{conn: conn} do
-      response =
-        conn
-        |> put_req_header("accept", "application/xml")
-        |> get("/users/jimm")
-        |> response(404)
-
-      assert response == ~S({"error":"Not found"})
-    end
-
-    test "activity+json format. it redirects on actual feed of user", %{conn: conn} do
-      note_activity = insert(:note_activity)
-      user = User.get_cached_by_ap_id(note_activity.data["actor"])
-
-      response =
-        conn
-        |> put_req_header("accept", "application/activity+json")
-        |> get("/users/#{user.nickname}")
-        |> json_response(200)
-
-      assert response["endpoints"] == %{
-               "oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize",
-               "oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps",
-               "oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token",
-               "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox",
-               "uploadMedia" => "#{Pleroma.Web.base_url()}/api/ap/upload_media"
-             }
-
-      assert response["@context"] == [
-               "https://www.w3.org/ns/activitystreams",
-               "http://localhost:4001/schemas/litepub-0.1.jsonld",
-               %{"@language" => "und"}
-             ]
-
-      assert Map.take(response, [
-               "followers",
-               "following",
-               "id",
-               "inbox",
-               "manuallyApprovesFollowers",
-               "name",
-               "outbox",
-               "preferredUsername",
-               "summary",
-               "tag",
-               "type",
-               "url"
-             ]) == %{
-               "followers" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/followers",
-               "following" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/following",
-               "id" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}",
-               "inbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/inbox",
-               "manuallyApprovesFollowers" => false,
-               "name" => user.name,
-               "outbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/outbox",
-               "preferredUsername" => user.nickname,
-               "summary" => user.bio,
-               "tag" => [],
-               "type" => "Person",
-               "url" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}"
-             }
-    end
-
-    test "activity+json format. it returns error whe use not found", %{conn: conn} do
-      response =
-        conn
-        |> put_req_header("accept", "application/activity+json")
-        |> get("/users/jimm")
-        |> json_response(404)
-
-      assert response == "Not found"
-    end
-
-    test "json format. it redirects on actual feed of user", %{conn: conn} do
-      note_activity = insert(:note_activity)
-      user = User.get_cached_by_ap_id(note_activity.data["actor"])
-
-      response =
-        conn
-        |> put_req_header("accept", "application/json")
-        |> get("/users/#{user.nickname}")
-        |> json_response(200)
-
-      assert response["endpoints"] == %{
-               "oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize",
-               "oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps",
-               "oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token",
-               "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox",
-               "uploadMedia" => "#{Pleroma.Web.base_url()}/api/ap/upload_media"
-             }
-
-      assert response["@context"] == [
-               "https://www.w3.org/ns/activitystreams",
-               "http://localhost:4001/schemas/litepub-0.1.jsonld",
-               %{"@language" => "und"}
-             ]
-
-      assert Map.take(response, [
-               "followers",
-               "following",
-               "id",
-               "inbox",
-               "manuallyApprovesFollowers",
-               "name",
-               "outbox",
-               "preferredUsername",
-               "summary",
-               "tag",
-               "type",
-               "url"
-             ]) == %{
-               "followers" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/followers",
-               "following" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/following",
-               "id" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}",
-               "inbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/inbox",
-               "manuallyApprovesFollowers" => false,
-               "name" => user.name,
-               "outbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/outbox",
-               "preferredUsername" => user.nickname,
-               "summary" => user.bio,
-               "tag" => [],
-               "type" => "Person",
-               "url" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}"
-             }
-    end
-
-    test "json format. it returns error whe use not found", %{conn: conn} do
-      response =
-        conn
-        |> put_req_header("accept", "application/json")
-        |> get("/users/jimm")
-        |> json_response(404)
-
-      assert response == "Not found"
-    end
-
-    test "html format. it redirects on actual feed of user", %{conn: conn} do
-      note_activity = insert(:note_activity)
-      user = User.get_cached_by_ap_id(note_activity.data["actor"])
-
-      response =
-        conn
-        |> get("/users/#{user.nickname}")
-        |> response(200)
-
-      assert response ==
-               Fallback.RedirectController.redirector_with_meta(
-                 conn,
-                 %{user: user}
-               ).resp_body
-    end
-
-    test "html format. it returns error when user not found", %{conn: conn} do
-      response =
-        conn
-        |> get("/users/jimm")
-        |> json_response(404)
-
-      assert response == %{"error" => "Not found"}
-    end
-  end
-
   describe "GET /notice/:id/embed_player" do
     test "render embed player", %{conn: conn} do
       note_activity = insert(:note_activity)