diff --git a/apps/content/lib/content/sitemap.ex b/apps/content/lib/content/sitemap.ex new file mode 100644 index 00000000..80edd50b --- /dev/null +++ b/apps/content/lib/content/sitemap.ex @@ -0,0 +1,45 @@ +defmodule Content.Sitemaps do + @moduledoc """ + This module generates sitemaps for the website and pings search engines as + appropriate. + """ + alias Content.{Endpoint, Post, Posts, Repo, Router.Helpers, Terms} + import Ecto.Query + + use Sitemap, + host: "https://#{Application.get_env(:content, Endpoint)[:url][:host]}", + files_path: "tmp/sitemap/", + public_path: "", + adapter: Content.SitemapStorage + + def generate do + create do + add "", priority: 0.5, changefreq: "hourly", expires: nil + + posts = + Posts.post_scope + |> where([p], p.type not in ["nav_menu_item", "attachment"]) + |> Repo.all() + + for post <- posts do + add Helpers.posts_path(Endpoint, :show, post), priority: 0.5, changefreq: "hourly", expires: nil + page_count = Post.content_page_count(post) + if page_count > 1 do + (2..page_count) + |> Enum.each(fn page -> + add Helpers.paged_post_path(Endpoint, :show, post, page), priority: 0.5, changefreq: "hourly", expires: nil + end) + end + end + + Terms.categories + |> Repo.all() + |> Enum.each(fn category -> + add Helpers.category_path(Endpoint, :index_posts, category.slug), priority: 0.5, changefreq: "hourly", expires: nil + end) + end + + # notify search engines (currently Google and Bing) of the updated sitemap + if Mix.env() == :prod, do: ping() + end +end diff --git a/apps/content/lib/content/sitemap_storage.ex b/apps/content/lib/content/sitemap_storage.ex new file mode 100644 index 00000000..5954b529 --- /dev/null +++ b/apps/content/lib/content/sitemap_storage.ex @@ -0,0 +1,69 @@ +defmodule Content.SitemapStorage do + @moduledoc """ + This module serves as a storage adapter for the Sitemap package. It writes + the sitemap as an attachment post into the system, so that the CMS will + serve it up. + """ + alias Content.{Endpoint, Post, Repo, Router.Helpers, User} + alias Ecto.Changeset + alias Sitemap.{Location} + import Ecto.Query + + @behaviour Sitemap.Adapters.Behaviour + + def write(name, data) do + path = Location.filename(name) + existing_post = Post |> from() |> where([name: ^path]) |> Repo.one() + + gzip? = Regex.match?(~r/.gz$/, path) + + mime = case gzip? do + true -> + "application/gzip" + false -> + "application/xml" + end + + data_processed = data |> :zlib.gzip() |> Base.encode64 + + post_params = %{ + date: NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second), + date_gmt: NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second), + content: data_processed, + title: path, + excerpt: "", + status: "publish", + comment_status: "closed", + ping_status: "closed", + password: "", + name: path, + to_ping: "", + pinged: "", + modified: NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second), + modified_gmt: NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second), + content_filtered: "", + parent: 0, + guid: Helpers.posts_url(Endpoint, :show, path), + menu_order: 0, + type: "attachment", + mime_type: mime, + comment_count: 0, + sticky: 0, + } + + case existing_post do + nil -> + %Post{} + |> Changeset.change(post_params) + |> (fn (post) -> + try do + post |> Repo.insert!() + rescue + Ecto.ConstraintError -> post |> Repo.update!() + end + end).() + post -> + post |> Changeset.change(post_params) |> Repo.update!() + end + end +end diff --git a/mix.lock b/mix.lock index 861590cc..6880c55a 100644 --- a/mix.lock +++ b/mix.lock @@ -54,7 +54,7 @@ "php_serializer": {:hex, :php_serializer, "0.9.2", "59c5fd6bd3096671fd89358fb8229341ac7423b50ad8d45a15213b02ea2edab2", [:mix], [], "hexpm", "34eb835a460944f7fc216773b363c02e7dcf8ac0390c9e9ccdbd92b31a7ca59a"}, "plug": {:hex, :plug, "1.10.4", "41eba7d1a2d671faaf531fa867645bd5a3dce0957d8e2a3f398ccff7d2ef017f", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad1e233fe73d2eec56616568d260777b67f53148a999dc2d048f4eb9778fe4a0"}, "plug_cowboy": {:hex, :plug_cowboy, "2.3.0", "149a50e05cb73c12aad6506a371cd75750c0b19a32f81866e1a323dda9e0e99d", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bc595a1870cef13f9c1e03df56d96804db7f702175e4ccacdb8fc75c02a7b97e"}, - "plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"}, + "plug_crypto": {:hex, :plug_crypto, "1.2.0", "1cb20793aa63a6c619dd18bb33d7a3aa94818e5fd39ad357051a67f26dfa2df6", [:mix], [], "hexpm", "a48b538ae8bf381ffac344520755f3007cc10bd8e90b240af98ea29b69683fc2"}, "postgrex": {:hex, :postgrex, "0.15.5", "aec40306a622d459b01bff890fa42f1430dac61593b122754144ad9033a2152f", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "ed90c81e1525f65a2ba2279dbcebf030d6d13328daa2f8088b9661eb9143af7f"}, "pow": {:hex, :pow, "1.0.20", "b99993811af5233681bfc521e81ca706d25a56f2be54bad6424db327ce840ab9", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.3.0 and < 1.6.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 2.0.0 and <= 3.0.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, ">= 1.5.0 and < 2.0.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "4b6bd271399ccb353abbdbdc316199fe7fd7ae36bbf47059d53e366831c34fc8"}, "quantum": {:hex, :quantum, "2.4.0", "f2ad4b20988f848455d35ed0e884ba0c7629a27ee86cbec6a6e0fc214b6e69cf", [:mix], [{:calendar, "~> 0.17", [hex: :calendar, repo: "hexpm", optional: true]}, {:crontab, "~> 1.1", [hex: :crontab, repo: "hexpm", optional: false]}, {:gen_stage, "~> 0.12 or ~> 1.0", [hex: :gen_stage, repo: "hexpm", optional: false]}, {:swarm, "~> 3.3", [hex: :swarm, repo: "hexpm", optional: false]}, {:timex, "~> 3.1", [hex: :timex, repo: "hexpm", optional: true]}, {:tzdata, "~> 1.0", [hex: :tzdata, repo: "hexpm", optional: true]}], "hexpm", "a125a9e65a5af740a1198f3b05c1a736fce3942f5e0dc2901e0f9be5745bea99"},