Implement RSS feeds
This commit is contained in:
parent
5942fe2d98
commit
617c510f94
11 changed files with 151 additions and 27 deletions
|
@ -3,18 +3,23 @@ defmodule Mebe2.Web.Router do
|
|||
use Raxx.Logger
|
||||
use Mebe2.Web.Middleware.RequestTime
|
||||
use Mebe2.Web.Middleware.Archives
|
||||
alias Mebe2.Web.Routes
|
||||
|
||||
use Raxx.Router, [
|
||||
{%{method: :GET, path: ["tag", _tag, "p", _page]}, Mebe2.Web.Routes.Tag},
|
||||
{%{method: :GET, path: ["tag", _tag]}, Mebe2.Web.Routes.Tag},
|
||||
{%{method: :GET, path: ["archive", _year, _month, "p", _page]}, Mebe2.Web.Routes.Month},
|
||||
{%{method: :GET, path: ["archive", _year, _month]}, Mebe2.Web.Routes.Month},
|
||||
{%{method: :GET, path: ["archive", _year, "p", _page]}, Mebe2.Web.Routes.Year},
|
||||
{%{method: :GET, path: ["archive", _year]}, Mebe2.Web.Routes.Year},
|
||||
{%{method: :GET, path: ["p", _page]}, Mebe2.Web.Routes.Index},
|
||||
{%{method: :GET, path: [_year, _month, _day, _slug]}, Mebe2.Web.Routes.Post},
|
||||
{%{method: :GET, path: [_slug]}, Mebe2.Web.Routes.Page},
|
||||
{%{method: :GET, path: []}, Mebe2.Web.Routes.Index},
|
||||
{_, Mebe2.Web.Routes.NotFound}
|
||||
{%{method: :GET, path: ["tag", _tag, "p", _page]}, Routes.Tag},
|
||||
{%{method: :GET, path: ["tag", _tag, "feed"]}, Routes.Tag},
|
||||
{%{method: :GET, path: ["tag", _tag]}, Routes.Tag},
|
||||
{%{method: :GET, path: ["archive", _year, _month, "p", _page]}, Routes.Month},
|
||||
{%{method: :GET, path: ["archive", _year, _month, "feed"]}, Routes.Month},
|
||||
{%{method: :GET, path: ["archive", _year, _month]}, Routes.Month},
|
||||
{%{method: :GET, path: ["archive", _year, "p", _page]}, Routes.Year},
|
||||
{%{method: :GET, path: ["archive", _year, "feed"]}, Routes.Year},
|
||||
{%{method: :GET, path: ["archive", _year]}, Routes.Year},
|
||||
{%{method: :GET, path: ["p", _page]}, Routes.Index},
|
||||
{%{method: :GET, path: ["feed"]}, Routes.Index},
|
||||
{%{method: :GET, path: [_year, _month, _day, _slug]}, Routes.Post},
|
||||
{%{method: :GET, path: [_slug]}, Routes.Page},
|
||||
{%{method: :GET, path: []}, Routes.Index},
|
||||
{_, Routes.NotFound}
|
||||
]
|
||||
end
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
defmodule Mebe2.Web.Routes.Index do
|
||||
use Raxx.Server
|
||||
alias Mebe2.Web.Views.Index
|
||||
alias Mebe2.Web.Routes.Utils
|
||||
|
||||
@impl Raxx.Server
|
||||
def handle_request(%Raxx.Request{path: ["p", page]} = _req, _state) do
|
||||
Mebe2.Web.Routes.Utils.render_posts(page, &post_getter/2, &Index.render/4)
|
||||
Utils.render_posts(page, &post_getter/2, &Index.render/4)
|
||||
end
|
||||
|
||||
@impl Raxx.Server
|
||||
def handle_request(%Raxx.Request{path: ["feed"]} = _req, _state) do
|
||||
Utils.render_feed(&post_getter/2)
|
||||
end
|
||||
|
||||
@impl Raxx.Server
|
||||
def handle_request(%Raxx.Request{} = _req, _state) do
|
||||
Mebe2.Web.Routes.Utils.render_posts(&post_getter/2, &Index.render/4)
|
||||
Utils.render_posts(&post_getter/2, &Index.render/4)
|
||||
end
|
||||
|
||||
defp post_getter(first, limit),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
defmodule Mebe2.Web.Routes.Month do
|
||||
use Raxx.Server
|
||||
alias Mebe2.Web.Routes.Utils
|
||||
|
||||
@impl Raxx.Server
|
||||
def handle_request(
|
||||
|
@ -8,13 +9,23 @@ defmodule Mebe2.Web.Routes.Month do
|
|||
) do
|
||||
with {year, _} <- Integer.parse(year_str),
|
||||
{month, _} <- Integer.parse(month_str) do
|
||||
Mebe2.Web.Routes.Utils.render_posts(
|
||||
Utils.render_posts(
|
||||
page,
|
||||
&post_getter(year, month, &1, &2),
|
||||
&renderer(year, month, &1, &2, &3, &4)
|
||||
)
|
||||
else
|
||||
_ -> Mebe2.Web.Routes.Utils.render_404()
|
||||
_ -> Utils.render_404()
|
||||
end
|
||||
end
|
||||
|
||||
@impl Raxx.Server
|
||||
def handle_request(%Raxx.Request{path: ["archive", year_str, month_str, "feed"]} = _req, _state) do
|
||||
with {year, _} <- Integer.parse(year_str),
|
||||
{month, _} <- Integer.parse(month_str) do
|
||||
Utils.render_feed(&post_getter(year, month, &1, &2))
|
||||
else
|
||||
_ -> Utils.render_404()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -22,12 +33,12 @@ defmodule Mebe2.Web.Routes.Month do
|
|||
def handle_request(%Raxx.Request{path: ["archive", year_str, month_str]} = _req, _state) do
|
||||
with {year, _} <- Integer.parse(year_str),
|
||||
{month, _} <- Integer.parse(month_str) do
|
||||
Mebe2.Web.Routes.Utils.render_posts(
|
||||
Utils.render_posts(
|
||||
&post_getter(year, month, &1, &2),
|
||||
&renderer(year, month, &1, &2, &3, &4)
|
||||
)
|
||||
else
|
||||
_ -> Mebe2.Web.Routes.Utils.render_404()
|
||||
_ -> Utils.render_404()
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,18 +1,24 @@
|
|||
defmodule Mebe2.Web.Routes.Tag do
|
||||
use Raxx.Server
|
||||
alias Mebe2.Web.Routes.Utils
|
||||
|
||||
@impl Raxx.Server
|
||||
def handle_request(%Raxx.Request{path: ["tag", tag, "p", page]} = _req, _state) do
|
||||
Mebe2.Web.Routes.Utils.render_posts(
|
||||
Utils.render_posts(
|
||||
page,
|
||||
&post_getter(tag, &1, &2),
|
||||
&renderer(tag, &1, &2, &3, &4)
|
||||
)
|
||||
end
|
||||
|
||||
@impl Raxx.Server
|
||||
def handle_request(%Raxx.Request{path: ["tag", tag, "feed"]} = _req, _state) do
|
||||
Utils.render_feed(&post_getter(tag, &1, &2))
|
||||
end
|
||||
|
||||
@impl Raxx.Server
|
||||
def handle_request(%Raxx.Request{path: ["tag", tag]} = _req, _state) do
|
||||
Mebe2.Web.Routes.Utils.render_posts(&post_getter(tag, &1, &2), &renderer(tag, &1, &2, &3, &4))
|
||||
Utils.render_posts(&post_getter(tag, &1, &2), &renderer(tag, &1, &2, &3, &4))
|
||||
end
|
||||
|
||||
defp post_getter(tag, first, limit),
|
||||
|
|
|
@ -20,10 +20,11 @@ defmodule Mebe2.Web.Routes.Utils do
|
|||
|
||||
@doc """
|
||||
Get post range for the given page, i.e. the starting post and limit to display on that page.
|
||||
You may optionally give the posts per page limit to use when constructing the range.
|
||||
"""
|
||||
@spec get_post_range(integer) :: {integer, integer}
|
||||
def get_post_range(page) when is_integer(page) do
|
||||
ppp = Mebe2.get_conf(:posts_per_page)
|
||||
@spec get_post_range(integer, integer | nil) :: {integer, integer}
|
||||
def get_post_range(page, posts_per_page \\ nil) when is_integer(page) do
|
||||
ppp = if is_nil(posts_per_page), do: Mebe2.get_conf(:posts_per_page), else: posts_per_page
|
||||
{(page - 1) * ppp, ppp}
|
||||
end
|
||||
|
||||
|
@ -31,7 +32,7 @@ defmodule Mebe2.Web.Routes.Utils do
|
|||
Render a set of posts on the given page.
|
||||
|
||||
- `page` is a string from the URL path.
|
||||
- `post_getter` isa function that gets the range of posts to load and returns a tuple where the
|
||||
- `post_getter` is a function that gets the range of posts to load and returns a tuple where the
|
||||
first element is the list of found posts, and the second element is the amount of all posts
|
||||
that are in the DB with these conditions.
|
||||
- `renderer` is a function that renders the content. Its arguments are the response to use,
|
||||
|
@ -53,6 +54,26 @@ defmodule Mebe2.Web.Routes.Utils do
|
|||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Render RSS feed for the given posts.
|
||||
|
||||
`post_getter` is a function that gets the range of posts to load and returns a tuple where the
|
||||
first element is the list of found posts and the second element can be anything (the structure)
|
||||
is the same as in `render_posts` so that the post getters can be reused.
|
||||
"""
|
||||
@spec render_feed((integer, integer -> {[Post.t()], any})) :: Raxx.Response.t()
|
||||
def render_feed(post_getter) do
|
||||
with true <- Mebe2.get_conf(:enable_feeds) do
|
||||
{first, limit} = get_post_range(1, Mebe2.get_conf(:posts_in_feed))
|
||||
{posts, _} = post_getter.(first, limit)
|
||||
|
||||
Raxx.response(200)
|
||||
|> Mebe2.Web.Views.Feeds.PostList.render(posts)
|
||||
else
|
||||
_ -> render_404()
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Render a 404 page.
|
||||
"""
|
||||
|
|
|
@ -1,28 +1,38 @@
|
|||
defmodule Mebe2.Web.Routes.Year do
|
||||
use Raxx.Server
|
||||
alias Mebe2.Web.Routes.Utils
|
||||
|
||||
@impl Raxx.Server
|
||||
def handle_request(%Raxx.Request{path: ["archive", year_str, "p", page]} = _req, _state) do
|
||||
with {year, _} <- Integer.parse(year_str) do
|
||||
Mebe2.Web.Routes.Utils.render_posts(
|
||||
Utils.render_posts(
|
||||
page,
|
||||
&post_getter(year, &1, &2),
|
||||
&renderer(year, &1, &2, &3, &4)
|
||||
)
|
||||
else
|
||||
_ -> Mebe2.Web.Routes.Utils.render_404()
|
||||
_ -> Utils.render_404()
|
||||
end
|
||||
end
|
||||
|
||||
@impl Raxx.Server
|
||||
def handle_request(%Raxx.Request{path: ["archive", year_str, "feed"]} = _req, _state) do
|
||||
with {year, _} <- Integer.parse(year_str) do
|
||||
Utils.render_feed(&post_getter(year, &1, &2))
|
||||
else
|
||||
_ -> Utils.render_404()
|
||||
end
|
||||
end
|
||||
|
||||
@impl Raxx.Server
|
||||
def handle_request(%Raxx.Request{path: ["archive", year_str]} = _req, _state) do
|
||||
with {year, _} <- Integer.parse(year_str) do
|
||||
Mebe2.Web.Routes.Utils.render_posts(
|
||||
Utils.render_posts(
|
||||
&post_getter(year, &1, &2),
|
||||
&renderer(year, &1, &2, &3, &4)
|
||||
)
|
||||
else
|
||||
_ -> Mebe2.Web.Routes.Utils.render_404()
|
||||
_ -> Utils.render_404()
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -3,6 +3,15 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title><%= Mebe2.get_conf(:blog_name) %></title>
|
||||
|
||||
<%= raw(if Mebe2.get_conf(:enable_feeds) do %>
|
||||
<link
|
||||
rel="alternate"
|
||||
type="application/rss+xml"
|
||||
title="RSS feed"
|
||||
href="<%= Mebe2.get_conf(:absolute_url) %>/feed"
|
||||
/>
|
||||
<% end) %>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
|
|
4
lib/web/views/feeds/base_layout.ex
Normal file
4
lib/web/views/feeds/base_layout.ex
Normal file
|
@ -0,0 +1,4 @@
|
|||
defmodule Mebe2.Web.Views.Feeds.BaseLayout do
|
||||
use Raxx.Layout,
|
||||
layout: "base_layout.xml.eex"
|
||||
end
|
11
lib/web/views/feeds/base_layout.xml.eex
Normal file
11
lib/web/views/feeds/base_layout.xml.eex
Normal file
|
@ -0,0 +1,11 @@
|
|||
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<channel>
|
||||
<title><%= Mebe2.get_conf(:blog_name) %></title>
|
||||
<link><%= Mebe2.get_conf(:absolute_url) %>/</link>
|
||||
<description></description>
|
||||
<generator>Mebe2</generator>
|
||||
<copyright>© <%= Mebe2.get_conf(:blog_author) %></copyright>
|
||||
|
||||
<%= __content__ %>
|
||||
</channel>
|
||||
</rss>
|
13
lib/web/views/feeds/post_list.ex
Normal file
13
lib/web/views/feeds/post_list.ex
Normal file
|
@ -0,0 +1,13 @@
|
|||
defmodule Mebe2.Web.Views.Feeds.PostList do
|
||||
use Mebe2.Web.Views.Feeds.BaseLayout,
|
||||
template: "post_list.xml.eex",
|
||||
arguments: [:posts]
|
||||
|
||||
@doc """
|
||||
Format datetime for RSS usage.
|
||||
"""
|
||||
@spec format_time(DateTime.t()) :: String.t()
|
||||
def format_time(datetime) do
|
||||
Calendar.Strftime.strftime!(datetime, "%a, %d %b %Y %T %z")
|
||||
end
|
||||
end
|
28
lib/web/views/feeds/post_list.xml.eex
Normal file
28
lib/web/views/feeds/post_list.xml.eex
Normal file
|
@ -0,0 +1,28 @@
|
|||
<%= raw(for post <- posts do %>
|
||||
<item>
|
||||
<title>
|
||||
<![CDATA[<%= raw(post.title) %>]]>
|
||||
</title>
|
||||
|
||||
<%= raw(if Mebe2.get_conf(:multi_author_mode) do %>
|
||||
<dc:creator><%= Mebe2.Engine.Utils.get_author(post) %></dc:creator>
|
||||
<% end) %>
|
||||
|
||||
<link><%= Mebe2.get_conf(:absolute_url) %><%= Mebe2.Web.Views.Utils.get_post_path(post) %></link>
|
||||
<guid><%= Mebe2.get_conf(:absolute_url) %><%= Mebe2.Web.Views.Utils.get_post_path(post) %></guid>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<%= raw(if Mebe2.get_conf(:show_full_content) do %>
|
||||
<%= raw(post.content) %>
|
||||
<% else %>
|
||||
<%= raw(post.short_content) %>
|
||||
<% end) %>
|
||||
|
||||
<a href="<%= Mebe2.get_conf(:absolute_url) %><%= Mebe2.Web.Views.Utils.get_post_path(post) %>">
|
||||
Read more…
|
||||
</a>
|
||||
]]>
|
||||
</description>
|
||||
<pubDate><%= format_time(post.datetime) %></pubDate>
|
||||
</item>
|
||||
<% end) %>
|
Reference in a new issue