defmodule BerrypodWeb.NavigationController do @moduledoc """ No-JS fallback for navigation form submission. With JS enabled, the LiveView handles everything. Without JS, the form POSTs here and we redirect back to the LiveView page. """ use BerrypodWeb, :controller alias Berrypod.Settings def save(conn, %{"header_nav" => header_json, "footer_nav" => footer_json}) do with {:ok, header_items} <- Jason.decode(header_json), {:ok, footer_items} <- Jason.decode(footer_json) do all_items = header_items ++ footer_items errors = validate_nav_items(all_items) if errors == [] do Settings.put_setting("header_nav", header_items, "json") Settings.put_setting("footer_nav", footer_items, "json") conn |> put_flash(:info, "Navigation saved") |> redirect(to: ~p"/admin/navigation") else conn |> put_flash(:error, Enum.join(errors, ". ")) |> redirect(to: ~p"/admin/navigation") end else {:error, _} -> conn |> put_flash(:error, "Invalid navigation data") |> redirect(to: ~p"/admin/navigation") end end defp validate_nav_items(items) do items |> Enum.flat_map(fn item -> cond do item["label"] == "" || item["label"] == nil -> ["All links need a label"] item["href"] == "" || item["href"] == nil -> ["\"#{item["label"]}\" needs a destination"] item["external"] == true || is_external_url?(item["href"]) -> if valid_url?(item["href"]) do [] else ["\"#{item["label"]}\" has an invalid URL"] end true -> [] end end) |> Enum.uniq() end defp valid_url?(url) when is_binary(url) do case URI.parse(url) do %URI{scheme: scheme, host: host} when scheme in ["http", "https"] and is_binary(host) and host != "" -> true _ -> false end end defp valid_url?(_), do: false defp is_external_url?(nil), do: false defp is_external_url?(""), do: false defp is_external_url?(href) do String.starts_with?(href, "http://") || String.starts_with?(href, "https://") end end