defmodule ImagineWeb.CmsRendererController do use ImagineWeb, :controller alias Imagine.CmsPages alias Imagine.CmsPages.{CmsPage, CmsPageVersion} alias Imagine.CmsTemplates alias Imagine.CmsTemplates.CmsTemplate alias Phoenix.HTML def show(conn, %{"path" => path_elements}) do case path_elements |> Enum.reverse() do ["edit", version, "version" | elements] -> path = elements |> Enum.reverse() |> Enum.join("/") cms_page = CmsPages.get_cms_page_by_path(path) redirect(conn, to: Routes.cms_page_path(conn, :edit_content, cms_page.id, version: version) ) [version, "version" | elements] -> if conn.assigns[:current_user] do path = elements |> Enum.reverse() |> Enum.join("/") render_cms_page(conn, path, version) else path = [version, "version" | elements] |> Enum.reverse() |> Enum.join("/") redirect(conn, to: Routes.auth_path(conn, :login, return_to: "/#{path}")) end ["login" | elements] -> path = elements |> Enum.reverse() |> Enum.join("/") redirect(conn, to: Routes.auth_path(conn, :login, return_to: "/#{path}")) ["edit" | elements] -> path = elements |> Enum.reverse() |> Enum.join("/") cms_page = CmsPages.get_cms_page_by_path(path) redirect(conn, to: Routes.cms_page_path(conn, :edit_content, cms_page.id)) _ -> path = path_elements |> Enum.join("/") render_cms_page(conn, path) end end defp render_cms_page(conn, path, version \\ 0) defp render_cms_page(%Plug.Conn{} = conn, path, version) do if cms_page = CmsPages.get_cms_page_by_path(path) do case cms_page.redirect_enabled do true -> redirect(conn, external: cms_page.redirect_to) _ -> check_cache_and_render_cms_page(conn, cms_page, version) end else render_404(conn) end end defp check_cache_and_render_cms_page(conn, cms_page, version) do render_uncached_cms_page(conn, cms_page, version) end defp render_uncached_cms_page(%Plug.Conn{} = conn, cms_page, version) do user = conn.assigns[:current_user] key = {{"cms_page", cms_page.id}} cached_result = if user, do: nil, else: Imagine.Cache.get(key) cms_page = cached_result || Imagine.Cache.set(key, CmsPages.preload_objects_and_versions(cms_page)) cms_page_version = get_requested_cms_page_version(cms_page, version) render_requested_cms_page_version(conn, cms_page, cms_page_version) end defp render_requested_cms_page_version(conn, nil, _), do: render_404(conn) defp render_requested_cms_page_version(conn, _, nil), do: render_404(conn) # note: cms_page_version is often a %CmsPage defp render_requested_cms_page_version(conn, %CmsPage{} = cms_page, cms_page_version) do user = conn.assigns[:current_user] key = {{"rendered_template_output", cms_page.id, cms_page_version.version}} cached_result = if user, do: nil, else: Imagine.Cache.get(key) output = case cached_result do nil -> cms_template_content = get_cms_template_content( cms_page, cms_page_version.version, cms_page_version.cms_template_id, cms_page_version.cms_template_version ) || get_cms_template_content(cms_page, 0, cms_page_version.cms_template_id, nil) output = CmsTemplate.render(:view, cms_template_content, cms_page_version, conn) Imagine.Cache.set(key, output) _ -> cached_result end if user do # for properties modal changeset = CmsPages.change_cms_page(cms_page) cms_templates = CmsTemplates.list_cms_templates() cms_pages = CmsPages.list_cms_pages() render(conn, "show.html", # layout: layout, output: output, cms_page: cms_page, changeset: changeset, cms_templates: cms_templates, cms_pages: cms_pages, display_version_options: version_option_tags(cms_page.versions, "Display: ", cms_page_version.version), published_version_options: version_option_tags(cms_page.versions, "Publish: ", cms_page.published_version), csrf_token: Phoenix.Controller.get_csrf_token(), action: Routes.cms_page_path(conn, :update, cms_page) ) else if cms_page.published_version == -1 do render_404(conn) else render(conn, "show.html", # layout: {ImagineWeb.LayoutView, "app.html"}, output: output, cms_page: cms_page ) end end end def render_404(conn) do # layout = {ImagineWeb.LayoutView, "app.html"} render(put_status(conn, :not_found), "404.html", cms_page: nil) # layout: layout) end defp get_requested_cms_page_version(nil, _), do: nil defp get_requested_cms_page_version(_, nil), do: nil defp get_requested_cms_page_version(cms_page, version_str) when is_binary(version_str) do case Integer.parse(version_str) do :error -> nil {version, _} -> get_requested_cms_page_version(cms_page, version) end end defp get_requested_cms_page_version(cms_page, 0), do: cms_page defp get_requested_cms_page_version(%CmsPage{version: pg_version} = cms_page, version) when version == pg_version, do: cms_page defp get_requested_cms_page_version(%CmsPage{} = cms_page, version) do CmsPages.get_cms_page_by_path_with_objects(cms_page.path, version) end # if page version is latest (0 or pg_version), use latest template version. # this is a legacy behavior, but one which I believe is still the least # surprising (no need to re-save all pages to see template changes) defp get_cms_template_content(%CmsPage{} = cms_page, 0, _, _), do: cms_page.cms_template.content_eex defp get_cms_template_content(%CmsPage{version: pg_version} = cms_page, version, _, _) when version == pg_version, do: cms_page.cms_template.content_eex defp get_cms_template_content(_, _, cms_template_id, cms_template_version) do CmsTemplates.get_cms_template_version_by( cms_template_id: cms_template_id, version: cms_template_version ).content_eex end defp version_option_tags(versions, prefix, selected) do version_tuples = Enum.map(versions, fn v -> {v.version, "#{prefix}#{v.version} - #{v.updated_on} by #{v.updated_by_username}"} end) version_tuples = case prefix do "Publish: " -> [{0, "Publish: [Latest]"}] ++ version_tuples ++ [{-1, "[Offline]"}] _ -> version_tuples end for {version, str} <- version_tuples, do: HTML.Tag.content_tag(:option, str, value: version, selected: version == selected) end end