defmodule Serum.Plugins.SitemapGenerator do
@moduledoc """
A Serum plugin that create a sitemap so that the search engine can index posts.
## Using the Plugin
# serum.exs:
%{
server_root: "https://example.io",
plugins: [
{Serum.Plugins.SitemapGenerator, only: :prod}
]
}
"""
@behaviour Serum.Plugin
serum_ver = Version.parse!(Mix.Project.config()[:version])
serum_req = "~> #{serum_ver.major}.#{serum_ver.minor}"
require EEx
alias Serum.GlobalBindings
alias Serum.Page
alias Serum.Post
def name, do: "Create sitemap for search engine"
def version, do: "1.2.0"
def elixir, do: ">= 1.8.0"
def serum, do: unquote(serum_req)
def description do
"Create a sitemap so that the search engine can index posts."
end
def implements, do: [build_succeeded: 3]
def build_succeeded(_src, dest, args) do
{pages, posts} = get_items(args[:for])
dest
|> create_file(pages, posts)
|> Serum.File.write()
|> case do
{:ok, _} -> :ok
{:error, _} = error -> error
end
end
@spec get_items(term()) :: {[Page.t()], [Post.t()]}
defp get_items(arg)
defp get_items(nil), do: get_items([:posts])
defp get_items(arg) when not is_list(arg), do: get_items([arg])
defp get_items(arg) do
pages = if :pages in arg, do: GlobalBindings.get(:all_pages), else: []
posts = if :posts in arg, do: GlobalBindings.get(:all_posts), else: []
{pages, posts}
end
sitemap_path =
:serum_md
|> :code.priv_dir()
|> Path.join("build_resources")
|> Path.join("sitemap.xml.eex")
EEx.function_from_file(:defp, :sitemap_xml, sitemap_path, [
:pages,
:posts,
:transformer,
:server_root
])
@spec create_file(binary(), [Page.t()], [Post.t()]) :: Serum.File.t()
defp create_file(dest, pages, posts) do
%Serum.File{
dest: Path.join(dest, "sitemap.xml"),
out_data: sitemap_xml(pages, posts, &to_w3c_format/1, get_server_root())
}
end
defp to_w3c_format(erl_datetime) do
# reference to https://www.w3.org/TR/NOTE-datetime
Timex.format!(erl_datetime, "%Y-%m-%d", :strftime)
end
defp get_server_root do
:site
|> GlobalBindings.get()
|> Map.fetch!(:server_root)
end
end