Packages

Ash extension for generating sitemaps

Current section

Files

Jump to
ash_sitemap lib build.ex
Raw

lib/build.ex

defmodule AshSitemap.Build do
@moduledoc """
Builder functions for generating xml sitemaps
"""
alias AshSitemap.Helpers
import XmlBuilder
@doc false
def to_xml(data) do
hostname = AshSitemap.Config.hostname()
elms =
element(
:url,
Helpers.eraser([
element(:loc, Path.join(hostname, data.path || "")),
element(
:lastmod,
Helpers.iso8601(Map.get(data, :lastmod, DateTime.utc_now()))
),
element(:expires, Map.get(data, :expires)),
element(:changefreq, Map.get(data, :changefreq)),
element(:priority, Map.get(data, :priority))
])
)
news = Map.get(data, :news)
images = Map.get(data, :image)
videos = Map.get(data, :video)
mobile = Map.get(data, :mobile)
geo = Map.get(data, :geo)
pagemap = Map.get(data, :pagemap)
elms = ifput(mobile, elms, &append_last(&1, mobile()))
elms = ifput(geo, elms, &append_last(&1, geo(geo)))
elms = ifput(news, elms, &append_last(&1, news(news)))
elms = ifput(images, elms, &append_last(&1, images(images)))
elms = ifput(videos, elms, &append_last(&1, videos(videos)))
elms = ifput(pagemap, elms, &append_last(&1, pagemap(pagemap)))
elms
end
@doc """
Generate a news sitemap element
"""
def news(data) do
element(
:"news:news",
Helpers.eraser([
element(
:"news:publication",
Helpers.eraser([
element(:"news:name", data[:publication_name]),
element(:"news:language", data[:publication_language])
])
),
element(:"news:title", data[:title]),
element(:"news:access", data[:access]),
element(:"news:genres", data[:genres]),
element(:"news:keywords", data[:keywords]),
element(:"news:stock_tickers", data[:stock_tickers]),
element(
:"news:publication_date",
Helpers.iso8601(data[:publication_date])
)
])
)
end
@doc """
Generate an image sitemap element
"""
def images(list, elements \\ [])
def images([], elements), do: elements
def images([{_, _} | _] = list, elements) do
images([list], elements)
end
def images([data | tail], elements) do
hostname = AshSitemap.Config.hostname()
elm =
element(
:"image:image",
Helpers.eraser([
element(:"image:loc", Path.join(hostname, data[:loc] || "")),
unless(is_nil(data[:title]),
do: element(:"image:title", data[:title])
),
unless(is_nil(data[:license]),
do: element(:"image:license", data[:license])
),
unless(is_nil(data[:caption]),
do: element(:"image:caption", data[:caption])
),
unless(is_nil(data[:geo_location]),
do: element(:"image:geo_location", data[:geo_location])
)
])
)
images(tail, elements ++ [elm])
end
@doc """
Generate a geo sitemap element
"""
def geo(data) do
element(:"geo:geo", [
element(:"geo:format", data[:format])
])
end
@doc """
Generate a mobile sitemap element
"""
def mobile do
element(:"mobile:mobile")
end
@doc """
Generate a pagemap sitemap element
"""
def pagemap(data) do
element(
:PageMap,
Enum.map(data[:dataobjects] || [], fn obj ->
element(
:DataObject,
%{type: obj[:type], id: obj[:id]},
Enum.map(obj[:attributes] || [], fn attr ->
element(:Attribute, %{name: attr[:name]}, attr[:value])
end)
)
end)
)
end
@doc """
Generates a sitemap index element
"""
def index(link, opts \\ []) do
element(
:sitemap,
Helpers.eraser([
element(
:loc,
if(opts[:host], do: Helpers.urljoin(link, opts[:host]), else: link)
),
element(
:lastmod,
Keyword.get_lazy(opts, :lastmod, fn -> Helpers.iso8601() end)
)
])
)
end
@doc """
Generate a video sitemap element
"""
def videos(list, elements \\ [])
def videos([], elements), do: elements
def videos([{_, _} | _] = list, elements) do
# Make sure keyword list
videos([list], elements)
end
def videos([data | tail], elements) do
elm =
element(
:"video:video",
Helpers.eraser([
element(:"video:title", data[:title]),
element(:"video:description", data[:description]),
if data[:player_loc] do
attrs = %{allow_embed: Helpers.yes_no(data[:allow_embed?])}
attrs =
ifput(
data[:autoplay?],
attrs,
&Map.put(&1, :autoplay, Helpers.autoplay(data[:autoplay?]))
)
element(:"video:player_loc", attrs, data[:player_loc])
end,
element(:"video:content_loc", data[:content_loc]),
element(:"video:thumbnail_loc", data[:thumbnail_loc]),
element(:"video:duration", data[:duration]),
element(:"video:expiration_date", data[:expiration_date]),
unless(is_nil(data[:gallery_loc]),
do:
element(
:"video:gallery_loc",
%{title: data[:gallery_title]},
data[:gallery_loc]
)
),
unless(is_nil(data[:rating]),
do: element(:"video:rating", data[:rating])
),
unless(is_nil(data[:view_count]),
do: element(:"video:view_count", data[:view_count])
),
unless(is_nil(data[:expiration_date]),
do:
element(
:"video:expiration_date",
Helpers.iso8601(data[:expiration_date])
)
),
unless(is_nil(data[:publication_date]),
do:
element(
:"video:publication_date",
Helpers.iso8601(data[:publication_date])
)
),
unless(is_nil(data[:tags]),
do: Enum.map(data[:tags] || [], &element(:"video:tag", &1))
),
unless(is_nil(data[:tag]), do: element(:"video:tag", data[:tag])),
unless(is_nil(data[:category]),
do: element(:"video:category", data[:category])
),
unless(is_nil(data[:family_friendly]),
do:
element(
:"video:family_friendly",
Helpers.yes_no(data[:family_friendly])
)
),
unless is_nil(data[:restriction]) do
attrs = %{relationship: Helpers.allow_deny(data[:relationship?])}
element(:"video:restriction", attrs, data[:restriction])
end,
unless is_nil(data[:price]) do
attrs = %{}
attrs =
ifput(
data[:price_currency],
attrs,
&Map.put(&1, :currency, data[:price_currency])
)
attrs =
ifput(
data[:price_type],
attrs,
&Map.put(&1, :type, data[:price_type])
)
attrs =
ifput(
data[:price_resolution],
attrs,
&Map.put(&1, :resolution, data[:price_resolution])
)
element(
:"video:price",
attrs,
data[:price]
)
end,
unless(is_nil(data[:requires_subscription?]),
do:
element(
:"video:requires_subscription",
Helpers.yes_no(data[:requires_subscription?])
)
),
unless is_nil(data[:uploader]) do
attrs = %{}
attrs =
ifput(
data[:uploader_info],
attrs,
&Map.put(&1, :info, data[:uploader_info])
)
element(:"video:uploader", attrs, data[:uploader])
end,
unless(is_nil(data[:price]),
do: element(:"video:price", video_price_attrs(data), data[:price])
),
unless(is_nil(data[:live?]),
do: element(:"video:live", Helpers.yes_no(data[:live?]))
),
unless(is_nil(data[:requires_subscription]),
do:
element(
:"video:requires_subscription",
Helpers.yes_no(data[:requires_subscription])
)
),
unless is_nil(data[:platform_list]) do
attrs = %{}
attrs =
ifput(
data[:platform_relationship?],
attrs,
&Map.put(
&1,
:relationship,
Helpers.yes_no(data[:platform_relationship?])
)
)
element(:"video:platform", attrs, data[:platform_list])
end,
unless is_nil(data[:show_title]) do
attrs = %{}
attrs =
ifput(
data[:show_title],
attrs,
&Map.put(
&1,
:relationship,
Helpers.yes_no(data[:show_title])
)
)
attrs =
ifput(
data[:show_video_type],
attrs,
&Map.put(
&1,
:video_type,
Helpers.yes_no(data[:show_video_type])
)
)
attrs =
ifput(
data[:show_episode_title],
attrs,
&Map.put(
&1,
:episode_title,
Helpers.yes_no(data[:show_episode_title])
)
)
attrs =
ifput(
data[:show_season_number],
attrs,
&Map.put(
&1,
:season_number,
Helpers.yes_no(data[:show_season_number])
)
)
attrs =
ifput(
data[:show_episode_number],
attrs,
&Map.put(
&1,
:episode_number,
Helpers.yes_no(data[:show_episode_number])
)
)
attrs =
ifput(
data[:show_premier_date],
attrs,
&Map.put(
&1,
:premier_date,
Helpers.yes_no(data[:show_premier_date])
)
)
element(:"video:tvshow", attrs, data[:show_title])
end
])
)
videos(tail, elements ++ [elm])
end
defp video_price_attrs(data) do
attrs = %{}
attrs = Map.put(attrs, :currency, data[:price_currency])
attrs =
ifput(data[:price_type], attrs, &Map.put(&1, :type, data[:price_type]))
attrs =
ifput(
data[:price_type],
attrs,
&Map.put(&1, :resolution, data[:price_resolution])
)
attrs
end
def alternates(list, elements \\ [])
def alternates([], elements), do: elements
def alternates([{_, _} | _] = list, elements) do
# Make sure keyword list
alternates([list], elements)
end
def alternates([data | tail], elements) do
rel = if data[:nofollow], do: "alternate nofollow", else: "alternate"
attrs = %{rel: rel, href: data[:href]}
attrs = Map.put(attrs, :hreflang, data[:lang])
attrs = Map.put(attrs, :media, data[:media])
alternates(tail, elements ++ [element(:"xhtml:link", attrs)])
end
defp ifput(bool, elements, fun) do
if bool do
fun.(elements)
else
elements
end
end
defp append_last(elements, element) do
combine = elem(elements, 2) ++ [element]
elements
|> Tuple.delete_at(2)
|> Tuple.insert_at(2, combine)
end
end