diff options
author | Sergey Suprunenko <suprunenko.s@gmail.com> | 2019-10-07 12:20:41 +0000 |
---|---|---|
committer | kaniini <ariadne@dereferenced.org> | 2019-10-07 12:20:41 +0000 |
commit | 4b3f77a99ff849f7593f63f7dccd206683b34d97 (patch) | |
tree | ee7ef95d8a9f017a79d22310e86b713ee2e924c4 /lib | |
parent | 5c32e6ee91b99200c33570017f6dc04bca8c1523 (diff) | |
download | pleroma-4b3f77a99ff849f7593f63f7dccd206683b34d97.tar.gz |
Extract RSS Feed functionality from OStatus
Diffstat (limited to 'lib')
-rw-r--r-- | lib/pleroma/emails/admin_email.ex | 2 | ||||
-rw-r--r-- | lib/pleroma/web/feed/feed_controller.ex | 63 | ||||
-rw-r--r-- | lib/pleroma/web/feed/feed_view.ex | 77 | ||||
-rw-r--r-- | lib/pleroma/web/metadata/feed.ex | 23 | ||||
-rw-r--r-- | lib/pleroma/web/ostatus/ostatus_controller.ex | 43 | ||||
-rw-r--r-- | lib/pleroma/web/router.ex | 5 | ||||
-rw-r--r-- | lib/pleroma/web/templates/feed/feed/_activity.xml.eex | 48 | ||||
-rw-r--r-- | lib/pleroma/web/templates/feed/feed/_author.xml.eex | 17 | ||||
-rw-r--r-- | lib/pleroma/web/templates/feed/feed/feed.xml.eex | 26 |
9 files changed, 259 insertions, 45 deletions
diff --git a/lib/pleroma/emails/admin_email.ex b/lib/pleroma/emails/admin_email.ex index 5a0903c13..b15e4041b 100644 --- a/lib/pleroma/emails/admin_email.ex +++ b/lib/pleroma/emails/admin_email.ex @@ -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 index 000000000..d91ecef9c --- /dev/null +++ b/lib/pleroma/web/feed/feed_controller.ex @@ -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 index 000000000..5eef1e757 --- /dev/null +++ b/lib/pleroma/web/feed/feed_view.ex @@ -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 index 000000000..8043e6c54 --- /dev/null +++ b/lib/pleroma/web/metadata/feed.ex @@ -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 diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex index 8f325b28e..20f2d9ddc 100644 --- a/lib/pleroma/web/ostatus/ostatus_controller.ex +++ b/lib/pleroma/web/ostatus/ostatus_controller.ex @@ -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 diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 675f485b2..ae799b8ac 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -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 index 000000000..d1f5e903c --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/_activity.xml.eex @@ -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 index 000000000..25cbffada --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/_author.xml.eex @@ -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 index 000000000..fbfdc46b5 --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/feed.xml.eex @@ -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> |