Page MenuHomePhorge

No OneTemporary

Size
34 KB
Referenced Files
None
Subscribers
None
diff --git a/changelog.d/rss.fix b/changelog.d/rss.fix
new file mode 100644
index 000000000..a3455698a
--- /dev/null
+++ b/changelog.d/rss.fix
@@ -0,0 +1 @@
+Fix RSS feeds to be W3C compliant and render better in RSS readers
diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex
index e1ee33d62..a433b8403 100644
--- a/lib/pleroma/web/feed/feed_view.ex
+++ b/lib/pleroma/web/feed/feed_view.ex
@@ -1,190 +1,331 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 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.Gettext
alias Pleroma.Web.MediaProxy
require Pleroma.Constants
@days ~w(Mon Tue Wed Thu Fri Sat Sun)
@months ~w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
def prepare_activity(activity, opts \\ []) do
object = Object.normalize(activity, fetch: false)
actor =
if opts[:actor] do
Pleroma.User.get_cached_by_ap_id(activity.actor)
end
%{
activity: activity,
object: object,
data: Map.get(object, :data),
actor: actor
}
end
def most_recent_update(activities) do
with %{updated_at: updated_at} <- List.first(activities) do
to_rfc3339(updated_at)
end
end
def most_recent_update(activities, user, :atom) do
(List.first(activities) || user).updated_at
|> to_rfc3339()
end
def most_recent_update(activities, user, :rss) do
(List.first(activities) || user).updated_at
|> to_rfc2822()
end
def feed_logo do
case Pleroma.Config.get([:feed, :logo]) do
nil ->
"#{Pleroma.Web.Endpoint.url()}/static/logo.svg"
logo ->
"#{Pleroma.Web.Endpoint.url()}#{logo}"
end
|> MediaProxy.url()
end
def email(user) do
user.nickname <> "@" <> Pleroma.Web.Endpoint.host()
end
def logo(user) do
user
|> User.avatar_url()
|> MediaProxy.url()
end
def last_activity(activities), do: List.last(activities)
def activity_title(%{"content" => content} = data, opts \\ %{}) do
summary = Map.get(data, "summary", "")
title =
cond do
summary != "" -> summary
content != "" -> activity_content(data)
true -> "a post"
end
title
|> Pleroma.Web.Metadata.Utils.scrub_html_and_truncate(opts[:max_length], opts[:omission])
|> HtmlEntities.encode()
end
def activity_description(data) do
content = activity_content(data)
summary = data["summary"]
cond do
content != "" -> escape(content)
summary != "" -> escape(summary)
true -> escape(data["type"])
end
end
+ def rss_content_encoded(data) do
+ data
+ |> feed_content_encoded()
+ |> String.replace("]]>", "]]&gt;")
+ end
+
+ def atom_content_encoded(data), do: feed_content_encoded(data)
+
+ defp feed_content_encoded(data) do
+ base_content =
+ case activity_content(data) do
+ "" ->
+ data["summary"] || data["type"] ||
+ ""
+ |> escape()
+
+ content ->
+ content
+ end
+
+ attachments_html =
+ (data["attachment"] || [])
+ |> Enum.map(&rss_attachment_preview/1)
+ |> Enum.reject(&(&1 == ""))
+ |> Enum.join("<br/><br/>")
+
+ [base_content, attachments_html]
+ |> Enum.reject(&(&1 == ""))
+ |> Enum.join("<br/><br/>")
+ end
+
+ defp rss_attachment_preview(attachment) do
+ href = attachment_href(attachment)
+
+ if is_binary(href) and href != "" do
+ media_type = attachment_type(attachment) || "application/octet-stream"
+ escaped_href = escape(href)
+ name = attachment["name"] || "Attachment"
+ escaped_name = escape(name)
+
+ cond do
+ String.starts_with?(media_type, "image/") ->
+ ~s(<p><img src="#{escaped_href}" alt="#{escaped_name}" loading="lazy"/></p>)
+
+ String.starts_with?(media_type, "video/") ->
+ ~s(<p><video controls preload="metadata" src="#{escaped_href}"></video></p><p><a href="#{escaped_href}">#{escaped_name}</a></p>)
+
+ String.starts_with?(media_type, "audio/") ->
+ ~s(<p><audio controls preload="metadata" src="#{escaped_href}"></audio></p><p><a href="#{escaped_href}">#{escaped_name}</a></p>)
+
+ true ->
+ ~s(<p><a href="#{escaped_href}">#{escaped_name}</a></p>)
+ end
+ else
+ ""
+ end
+ end
+
def activity_content(%{"content" => content}) do
content
|> String.replace(~r/[\n\r]/, "")
end
def activity_content(_), do: ""
def activity_context(activity), do: escape(activity.data["context"])
+ def feed_self_url(conn), do: Phoenix.Controller.current_url(conn)
+
+ def activity_link(activity, data) do
+ cond do
+ activity.local -> data["id"] || data["url"]
+ true -> data["external_url"] || data["id"] || data["url"]
+ end
+ end
+
def attachment_href(attachment) do
- attachment["url"]
- |> hd()
+ attachment
+ |> attachment_url_data()
|> Map.get("href")
end
def attachment_type(attachment) do
- attachment["url"]
- |> hd()
- |> Map.get("mediaType")
+ attachment
+ |> attachment_url_data()
+ |> Map.get("mediaType") || attachment["mediaType"]
+ end
+
+ def attachment_size(attachment) do
+ attachment
+ |> attachment_url_data()
+ |> Map.get("size", 0)
+ end
+
+ def attachment_size_positive(attachment) do
+ case attachment_size(attachment) do
+ size when is_integer(size) and size > 0 ->
+ size
+
+ size when is_binary(size) ->
+ case Integer.parse(size) do
+ {parsed, ""} when parsed > 0 -> parsed
+ _ -> nil
+ end
+
+ _ ->
+ nil
+ end
+ end
+
+ def attachment_medium(attachment) do
+ case attachment_type(attachment) do
+ "image/" <> _rest -> "image"
+ "video/" <> _rest -> "video"
+ "audio/" <> _rest -> "audio"
+ _ -> "document"
+ end
+ end
+
+ def attachment_previewable?(attachment) do
+ attachment_medium(attachment) in ["image", "video", "audio"]
+ end
+
+ def attachment_description(attachment) do
+ attachment["name"] || ""
+ end
+
+ def media_content_xml(attachment) do
+ href = escape(attachment_href(attachment) || "")
+ type = escape(attachment_type(attachment) || "application/octet-stream")
+ medium = escape(attachment_medium(attachment))
+
+ file_size_attr =
+ case attachment_size_positive(attachment) do
+ size when is_integer(size) -> ~s( fileSize="#{size}")
+ _ -> ""
+ end
+
+ description_xml =
+ case attachment_description(attachment) do
+ "" ->
+ ""
+
+ description ->
+ ~s(\n <media:description type="plain">#{escape(description)}</media:description>)
+ end
+
+ """
+ <media:content url="#{href}" type="#{type}"#{file_size_attr} medium="#{medium}">
+ <media:rating scheme="urn:simple">nonadult</media:rating>#{description_xml}
+ </media:content>
+ """
+ end
+
+ defp attachment_url_data(attachment) do
+ case attachment["url"] do
+ [first | _] when is_map(first) -> first
+ %{} = map -> map
+ _ -> %{}
+ end
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
@spec to_rfc3339(String.t() | NaiveDateTime.t()) :: String.t()
def to_rfc3339(date) when is_binary(date) do
date
|> Timex.parse!("{ISO:Extended}")
|> to_rfc3339()
end
def to_rfc3339(nd) do
nd
|> Timex.to_datetime()
|> Timex.format!("{RFC3339}")
end
@spec to_rfc2822(String.t() | DateTime.t() | NaiveDateTime.t()) :: String.t()
def to_rfc2822(datestr) when is_binary(datestr) do
datestr
|> Timex.parse!("{ISO:Extended}")
|> to_rfc2822()
end
def to_rfc2822(%DateTime{} = date) do
date
|> DateTime.to_naive()
|> NaiveDateTime.to_erl()
|> rfc2822_from_erl()
end
def to_rfc2822(nd) do
nd
|> Timex.to_datetime()
|> DateTime.to_naive()
|> NaiveDateTime.to_erl()
|> rfc2822_from_erl()
end
@doc """
Builds a RFC2822 timestamp from an Erlang timestamp
[RFC2822 3.3 - Date and Time Specification](https://tools.ietf.org/html/rfc2822#section-3.3)
This function always assumes the Erlang timestamp is in Universal time, not Local time
"""
def rfc2822_from_erl({{year, month, day} = date, {hour, minute, second}}) do
day_name = Enum.at(@days, :calendar.day_of_the_week(date) - 1)
month_name = Enum.at(@months, month - 1)
date_part = "#{day_name}, #{day} #{month_name} #{year}"
time_part = "#{pad(hour)}:#{pad(minute)}:#{pad(second)}"
date_part <> " " <> time_part <> " +0000"
end
defp pad(num) do
num
|> Integer.to_string()
|> String.pad_leading(2, "0")
end
end
diff --git a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex
index b774f7984..f87d97cb4 100644
--- a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex
+++ b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex
@@ -1,50 +1,38 @@
<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><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
- <content type="html"><%= activity_description(@data) %></content>
+ <content type="html"><%= escape(atom_content_encoded(@data)) %></content>
<published><%= to_rfc3339(@data["published"]) %></published>
<updated><%= to_rfc3339(@data["published"]) %></updated>
<ostatus:conversation ref="<%= activity_context(@activity) %>">
<%= activity_context(@activity) %>
</ostatus:conversation>
<link href="<%= activity_context(@activity) %>" rel="ostatus:conversation"/>
<%= if @data["summary"] != "" do %>
<summary><%= escape(@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 %>
+ <link type="application/atom+xml" href='<%= activity_link(@activity, @data) %>' rel="self"/>
+ <link type="text/html" href='<%= activity_link(@activity, @data) %>' rel="alternate"/>
<%= for tag <- Pleroma.Object.hashtags(@object) do %>
<category term="<%= tag %>"></category>
<% end %>
<%= for attachment <- @data["attachment"] || [] do %>
- <link rel="enclosure" href="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>"/>
+ <%= unless attachment_previewable?(attachment) do %>
+ <link rel="enclosure" href="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>" length="<%= attachment_size(attachment) %>"/>
+ <% end %>
<% 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.Endpoint.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/_activity.rss.eex b/lib/pleroma/web/templates/feed/feed/_activity.rss.eex
index 7de98f736..a8c2aa342 100644
--- a/lib/pleroma/web/templates/feed/feed/_activity.rss.eex
+++ b/lib/pleroma/web/templates/feed/feed/_activity.rss.eex
@@ -1,45 +1,32 @@
<item>
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
- <guid><%= @data["id"] %></guid>
+ <guid isPermaLink="true"><%= @data["id"] %></guid>
<title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
<description><%= activity_description(@data) %></description>
+ <content:encoded><![CDATA[<%= rss_content_encoded(@data) %>]]></content:encoded>
<pubDate><%= to_rfc2822(@data["published"]) %></pubDate>
<ostatus:conversation ref="<%= activity_context(@activity) %>">
<%= activity_context(@activity) %>
</ostatus:conversation>
- <%= if @activity.local do %>
- <link><%= @data["id"] %></link>
- <% else %>
- <link><%= @data["external_url"] %></link>
- <% end %>
-
- <link rel="ostatus:conversation"><%= activity_context(@activity) %></link>
+ <link><%= activity_link(@activity, @data) %></link>
<%= for tag <- Pleroma.Object.hashtags(@object) do %>
- <category term="<%= tag %>"></category>
+ <%= if tag && tag != "" do %>
+ <category><%= tag %></category>
+ <% end %>
<% end %>
<%= for attachment <- @data["attachment"] || [] do %>
- <enclosure url="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>" />
+ <%= size = attachment_size_positive(attachment) %>
+ <%= unless attachment_previewable?(attachment) do %>
+ <enclosure url="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>" length="<%= size || 0 %>" />
+ <% end %>
+ <%= media_content_xml(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">http://activityschema.org/collection/public</link>
- <% else %>
- <%= unless Regex.match?(~r/^#{Pleroma.Web.Endpoint.url()}.+followers$/, id) do %>
- <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person"><%= id %></link>
- <% end %>
- <% end %>
- <% end %>
-
- <%= for {emoji, file} <- @data["emoji"] || %{} do %>
- <link name="<%= emoji %>" rel="emoji"><%= file %></link>
- <% end %>
</item>
diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex
index 03c222975..ae762f9ed 100644
--- a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex
+++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex
@@ -1,49 +1,37 @@
<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>
<%= render Phoenix.Controller.view_module(@conn), "_tag_author.atom", assigns %>
<id><%= @data["id"] %></id>
<title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
- <content type="html"><%= activity_description(@data) %></content>
+ <content type="html"><%= escape(atom_content_encoded(@data)) %></content>
<published><%= to_rfc3339(@data["published"]) %></published>
<updated><%= to_rfc3339(@data["published"]) %></updated>
<ostatus:conversation ref="<%= activity_context(@activity) %>">
<%= activity_context(@activity) %>
</ostatus:conversation>
<link href="<%= activity_context(@activity) %>" rel="ostatus:conversation"/>
<%= if @data["summary"] != "" do %>
- <summary><%= @data["summary"] %></summary>
+ <summary><%= escape(@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 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.Endpoint.url()}.+followers$/, id) do %>
- <link rel="mentioned"
- ostatus:object-type="http://activitystrea.ms/schema/1.0/person"
- href="<%= id %>" />
- <% end %>
- <% end %>
- <% end %>
+ <link type="application/atom+xml" href='<%= activity_link(@activity, @data) %>' rel="self"/>
+ <link type="text/html" href='<%= activity_link(@activity, @data) %>' rel="alternate"/>
<%= for tag <- Pleroma.Object.hashtags(@object) do %>
<category term="<%= tag %>"></category>
<% end %>
+ <%= for attachment <- @data["attachment"] || [] do %>
+ <%= unless attachment_previewable?(attachment) do %>
+ <link rel="enclosure" href="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>" length="<%= attachment_size(attachment) %>"/>
+ <% 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/_tag_activity.xml.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex
index 1b8c34b87..6ae3c5a55 100644
--- a/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex
+++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex
@@ -1,14 +1,22 @@
<item>
- <title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
-
-
- <guid isPermalink="true"><%= activity_context(@activity) %></guid>
- <link><%= activity_context(@activity) %></link>
+ <guid isPermalink="true"><%= activity_link(@activity, @data) %></guid>
+ <link><%= activity_link(@activity, @data) %></link>
<pubDate><%= to_rfc2822(@data["published"]) %></pubDate>
-
+ <title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
<description><%= activity_description(@data) %></description>
+ <content:encoded><![CDATA[<%= rss_content_encoded(@data) %>]]></content:encoded>
+
<%= for attachment <- @data["attachment"] || [] do %>
- <enclosure url="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>"/>
+ <%= size = attachment_size_positive(attachment) %>
+ <%= unless attachment_previewable?(attachment) do %>
+ <enclosure url="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>" length="<%= size || 0 %>"/>
+ <% end %>
+ <%= media_content_xml(attachment) %>
<% end %>
+ <%= for tag <- Pleroma.Object.hashtags(@object) do %>
+ <%= if tag && tag != "" do %>
+ <category><%= tag %></category>
+ <% end %>
+ <% end %>
</item>
diff --git a/lib/pleroma/web/templates/feed/feed/tag.atom.eex b/lib/pleroma/web/templates/feed/feed/tag.atom.eex
index 3449c97ff..1646aeec7 100644
--- a/lib/pleroma/web/templates/feed/feed/tag.atom.eex
+++ b/lib/pleroma/web/templates/feed/feed/tag.atom.eex
@@ -1,20 +1,22 @@
<?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"
xmlns:statusnet="http://status.net/schema/api/1/">
<id><%= Routes.tag_feed_url(@conn, :feed, @tag) <> ".atom" %></id>
<title>#<%= @tag %></title>
- <subtitle><%= Gettext.dpgettext("static_pages", "tag feed description", "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse.", tag: @tag) %></subtitle>
+ <%= with subtitle when subtitle not in [nil, ""] <- Gettext.dpgettext("static_pages", "tag feed description", "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse.", tag: @tag) do %>
+ <subtitle><%= subtitle %></subtitle>
+ <% end %>
<logo><%= feed_logo() %></logo>
<updated><%= most_recent_update(@activities) %></updated>
- <link rel="self" href="<%= "#{Routes.tag_feed_url(@conn, :feed, @tag)}.atom" %>" type="application/atom+xml"/>
+ <link rel="self" href="<%= feed_self_url(@conn) %>" type="application/atom+xml"/>
<%= for activity <- @activities do %>
<%= render Phoenix.Controller.view_module(@conn), "_tag_activity.atom", Map.merge(assigns, prepare_activity(activity, actor: true)) %>
<% end %>
</feed>
diff --git a/lib/pleroma/web/templates/feed/feed/tag.rss.eex b/lib/pleroma/web/templates/feed/feed/tag.rss.eex
index a87f9bf50..618b89ba3 100644
--- a/lib/pleroma/web/templates/feed/feed/tag.rss.eex
+++ b/lib/pleroma/web/templates/feed/feed/tag.rss.eex
@@ -1,16 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
xmlns:webfeeds="http://webfeeds.org/rss/1.0"
+ xmlns:content="http://purl.org/rss/1.0/modules/content/"
+ xmlns:media="http://search.yahoo.com/mrss/"
xmlns:thr="http://purl.org/syndication/thread/1.0">
<channel>
<title>#<%= @tag %></title>
<description><%= Gettext.dpgettext("static_pages", "tag feed description", "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse.", tag: @tag) %></description>
<link><%= "#{Routes.tag_feed_url(@conn, :feed, @tag)}.rss" %></link>
<webfeeds:logo><%= feed_logo() %></webfeeds:logo>
+ <webfeeds:icon><%= feed_logo() %></webfeeds:icon>
<webfeeds:accentColor>2b90d9</webfeeds:accentColor>
+ <lastBuildDate><%= most_recent_update(@activities) || to_rfc2822(NaiveDateTime.utc_now()) %></lastBuildDate>
+ <generator><%= Pleroma.Application.named_version() %></generator>
<%= for activity <- @activities do %>
<%= render Phoenix.Controller.view_module(@conn), "_tag_activity.xml", Map.merge(assigns, prepare_activity(activity)) %>
<% end %>
</channel>
</rss>
diff --git a/lib/pleroma/web/templates/feed/feed/user.atom.eex b/lib/pleroma/web/templates/feed/feed/user.atom.eex
index c2c77cfed..cc6883727 100644
--- a/lib/pleroma/web/templates/feed/feed/user.atom.eex
+++ b/lib/pleroma/web/templates/feed/feed/user.atom.eex
@@ -1,25 +1,27 @@
<?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><%= Routes.user_feed_url(@conn, :feed, @user.nickname) <> ".atom" %></id>
<title><%= @user.nickname <> "'s timeline" %></title>
- <subtitle><%= escape(@user.bio) %></subtitle>
+ <%= if @user.bio && @user.bio != "" do %>
+ <subtitle><%= escape(@user.bio) %></subtitle>
+ <% end %>
<updated><%= most_recent_update(@activities, @user, :atom) %></updated>
<logo><%= logo(@user) %></logo>
- <link rel="self" href="<%= "#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.atom" %>" type="application/atom+xml"/>
+ <link rel="self" href="<%= feed_self_url(@conn) %>" type="application/atom+xml"/>
<%= render Phoenix.Controller.view_module(@conn), "_author.atom", assigns %>
<%= if last_activity(@activities) do %>
<link rel="next" href="<%= "#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.atom?max_id=#{last_activity(@activities).id}" %>" type="application/atom+xml"/>
<% end %>
<%= for activity <- @activities do %>
<%= render Phoenix.Controller.view_module(@conn), "_activity.atom", Map.merge(assigns, prepare_activity(activity)) %>
<% end %>
</feed>
diff --git a/lib/pleroma/web/templates/feed/feed/user.rss.eex b/lib/pleroma/web/templates/feed/feed/user.rss.eex
index b907a7e57..e2150d8b6 100644
--- a/lib/pleroma/web/templates/feed/feed/user.rss.eex
+++ b/lib/pleroma/web/templates/feed/feed/user.rss.eex
@@ -1,30 +1,36 @@
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0"
xmlns:atom="http://www.w3.org/2005/Atom"
+ xmlns:content="http://purl.org/rss/1.0/modules/content/"
+ xmlns:webfeeds="http://webfeeds.org/rss/1.0"
+ xmlns:media="http://search.yahoo.com/mrss/"
xmlns:thr="http://purl.org/syndication/thread/1.0"
xmlns:activity="http://activitystrea.ms/spec/1.0/"
xmlns:ostatus="http://ostatus.org/schema/1.0"
xmlns:poco="http://portablecontacts.net/spec/1.0">
<channel>
<title><%= @user.nickname <> "'s timeline" %></title>
- <link><%= "#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss" %></link>
- <atom:link href="<%= Routes.user_feed_url(@conn, :feed, @user.nickname) <> ".atom" %>"
+ <atom:link href="<%= feed_self_url(@conn) %>"
rel="self" type="application/rss+xml" />
+ <link><%= "#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss" %></link>
<description><%= escape(@user.bio) %></description>
<image>
<url><%= logo(@user) %></url>
<title><%= @user.nickname <> "'s timeline" %></title>
<link><%= "#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss" %></link>
</image>
+ <lastBuildDate><%= most_recent_update(@activities, @user, :rss) %></lastBuildDate>
+ <webfeeds:icon><%= logo(@user) %></webfeeds:icon>
+ <generator><%= Pleroma.Application.named_version() %></generator>
<%= render Phoenix.Controller.view_module(@conn), "_author.rss", assigns %>
<%= if last_activity(@activities) do %>
- <link rel="next"><%= "#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss?max_id=#{last_activity(@activities).id}" %></link>
+ <atom:link rel="next" href="<%= "#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss?max_id=#{last_activity(@activities).id}" %>" />
<% end %>
<%= for activity <- @activities do %>
<%= render Phoenix.Controller.view_module(@conn), "_activity.rss", Map.merge(assigns, prepare_activity(activity)) %>
<% end %>
</channel>
</rss>
diff --git a/test/pleroma/web/feed/tag_controller_test.exs b/test/pleroma/web/feed/tag_controller_test.exs
index 662235f31..7f2f57b07 100644
--- a/test/pleroma/web/feed/tag_controller_test.exs
+++ b/test/pleroma/web/feed/tag_controller_test.exs
@@ -1,250 +1,250 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Feed.TagControllerTest do
use Pleroma.Web.ConnCase
import Pleroma.Factory
import SweetXml
alias Pleroma.Object
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Feed.FeedView
setup do: clear_config([:feed])
test "gets a feed (ATOM)", %{conn: conn} do
clear_config(
[:feed, :post_title],
%{max_length: 25, omission: "..."}
)
user = insert(:user)
{:ok, activity1} = CommonAPI.post(user, %{status: "yeah #PleromaArt"})
object = Object.normalize(activity1, fetch: false)
object_data =
Map.put(object.data, "attachment", [
%{
"url" => [
%{
"href" =>
"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
"mediaType" => "video/mp4",
"type" => "Link"
}
]
}
])
object
|> Ecto.Changeset.change(data: object_data)
|> Pleroma.Repo.update()
{:ok, activity2} = CommonAPI.post(user, %{status: "42 This is :moominmamma #PleromaArt"})
{:ok, _activity3} = CommonAPI.post(user, %{status: "This is :moominmamma"})
response =
conn
|> put_req_header("accept", "application/atom+xml")
|> get(tag_feed_path(conn, :feed, "pleromaart.atom"))
|> response(200)
xml = parse(response)
assert xpath(xml, ~x"//feed/title/text()") == ~c"#pleromaart"
assert xpath(xml, ~x"//feed/entry/title/text()"l) == [
~c"42 This is :moominmamm...",
~c"yeah #PleromaArt"
]
assert xpath(xml, ~x"//feed/entry/author/name/text()"ls) == [user.nickname, user.nickname]
conn =
conn
|> put_req_header("accept", "application/atom+xml")
|> get("/tags/pleromaart.atom", %{"max_id" => activity2.id})
assert get_resp_header(conn, "content-type") == ["application/atom+xml; charset=utf-8"]
resp = response(conn, 200)
xml = parse(resp)
assert xpath(xml, ~x"//feed/title/text()") == ~c"#pleromaart"
assert xpath(xml, ~x"//feed/entry/title/text()"l) == [
~c"yeah #PleromaArt"
]
end
test "gets a feed (RSS)", %{conn: conn} do
clear_config(
[:feed, :post_title],
%{max_length: 25, omission: "..."}
)
user = insert(:user)
{:ok, activity1} = CommonAPI.post(user, %{status: "yeah #PleromaArt"})
object = Object.normalize(activity1, fetch: false)
object_data =
Map.put(object.data, "attachment", [
%{
"url" => [
%{
"href" =>
"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
"mediaType" => "video/mp4",
"type" => "Link"
}
]
}
])
object
|> Ecto.Changeset.change(data: object_data)
|> Pleroma.Repo.update()
{:ok, activity2} = CommonAPI.post(user, %{status: "42 This is :moominmamma #PleromaArt"})
{:ok, _activity3} = CommonAPI.post(user, %{status: "This is :moominmamma"})
response =
conn
|> put_req_header("accept", "application/rss+xml")
|> get(tag_feed_path(conn, :feed, "pleromaart.rss"))
|> response(200)
xml = parse(response)
assert xpath(xml, ~x"//channel/title/text()") == ~c"#pleromaart"
assert xpath(xml, ~x"//channel/description/text()"s) ==
"These are public toots tagged with #pleromaart. You can interact with them if you have an account anywhere in the fediverse."
assert xpath(xml, ~x"//channel/link/text()") ==
~c"#{Pleroma.Web.Endpoint.url()}/tags/pleromaart.rss"
assert xpath(xml, ~x"//channel/webfeeds:logo/text()") ==
~c"#{Pleroma.Web.Endpoint.url()}/static/logo.svg"
assert xpath(xml, ~x"//channel/item/title/text()"l) == [
~c"42 This is :moominmamm...",
~c"yeah #PleromaArt"
]
assert xpath(xml, ~x"//channel/item/pubDate/text()"sl) == [
FeedView.to_rfc2822(activity2.data["published"]),
FeedView.to_rfc2822(activity1.data["published"])
]
- assert xpath(xml, ~x"//channel/item/enclosure/@url"sl) == [
+ assert xpath(xml, ~x"//channel/item/media:content/@url"sl) == [
"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4"
]
obj1 = Object.normalize(activity1, fetch: false)
obj2 = Object.normalize(activity2, fetch: false)
assert xpath(xml, ~x"//channel/item/description/text()"sl) == [
HtmlEntities.decode(FeedView.activity_content(obj2.data)),
HtmlEntities.decode(FeedView.activity_content(obj1.data))
]
response =
conn
|> put_req_header("accept", "application/rss+xml")
|> get(tag_feed_path(conn, :feed, "pleromaart.rss"))
|> response(200)
xml = parse(response)
assert xpath(xml, ~x"//channel/title/text()") == ~c"#pleromaart"
assert xpath(xml, ~x"//channel/description/text()"s) ==
"These are public toots tagged with #pleromaart. You can interact with them if you have an account anywhere in the fediverse."
conn =
conn
|> put_req_header("accept", "application/rss+xml")
|> get("/tags/pleromaart.rss", %{"max_id" => activity2.id})
assert get_resp_header(conn, "content-type") == ["application/rss+xml; charset=utf-8"]
resp = response(conn, 200)
xml = parse(resp)
assert xpath(xml, ~x"//channel/title/text()") == ~c"#pleromaart"
assert xpath(xml, ~x"//channel/item/title/text()"l) == [
~c"yeah #PleromaArt"
]
end
describe "private instance" do
setup do: clear_config([:instance, :public], false)
test "returns 404 for tags feed", %{conn: conn} do
conn
|> put_req_header("accept", "application/rss+xml")
|> get(tag_feed_path(conn, :feed, "pleromaart.rss"))
|> response(404)
end
end
describe "restricted for unauthenticated" do
test "returns 404 when local timeline is disabled", %{conn: conn} do
clear_config([:restrict_unauthenticated, :timelines], %{local: true, federated: false})
conn
|> put_req_header("accept", "application/rss+xml")
|> get(tag_feed_path(conn, :feed, "pleromaart.rss"))
|> response(404)
end
test "returns local posts only when federated timeline is disabled", %{conn: conn} do
clear_config([:restrict_unauthenticated, :timelines], %{local: false, federated: true})
local_user = insert(:user)
remote_user = insert(:user, local: false)
local_note =
insert(:note,
user: local_user,
data: %{
"content" => "local post #PleromaArt",
"summary" => "",
"tag" => ["pleromaart"]
}
)
remote_note =
insert(:note,
user: remote_user,
data: %{
"content" => "remote post #PleromaArt",
"summary" => "",
"tag" => ["pleromaart"]
},
local: false
)
insert(:note_activity, user: local_user, note: local_note)
insert(:note_activity, user: remote_user, note: remote_note, local: false)
response =
conn
|> put_req_header("accept", "application/rss+xml")
|> get(tag_feed_path(conn, :feed, "pleromaart.rss"))
|> response(200)
xml = parse(response)
assert xpath(xml, ~x"//channel/title/text()") == ~c"#pleromaart"
assert xpath(xml, ~x"//channel/item/title/text()"l) == [
~c"local post #PleromaArt"
]
end
end
end

File Metadata

Mime Type
text/x-diff
Expires
Sat, Jul 18, 1:37 PM (1 d, 4 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1693656
Default Alt Text
(34 KB)

Event Timeline