defmodule Toolbox.Notifications.TemplateHelper do @moduledoc """ Set of utility functions for use in notification templates. """ alias Timex.Format.DateTime.Formatter alias Timex.Timezone @doc """ Formats timestamp into the given format in the given timezone. """ def format_ts(ts, format_str, target_tz \\ "Etc/UTC") def format_ts(nil, _, _) do nil end def format_ts(ts, format_str, target_tz) do ts |> DateTime.from_unix!(:millisecond) |> Timezone.convert(target_tz) |> Formatter.format!(format_str) end @doc """ Returns the UI url. """ def ui_url do Application.fetch_env!(:toolbox, :ui_url) end @doc """ Returns a URL to the asset detail. The type of UI can be specified, and the link is generated to work with that UI. Can be `:new` or `:old`. """ @spec asset_link(String.t(), :new | :old) :: String.t() def asset_link(asset_id, ui_type \\ :new) def asset_link(asset_id, :new) do Path.join([ ui_url(), "network", asset_id ]) end def asset_link(asset_id, :old) do Path.join([ ui_url(), "/assets/browser", asset_id ]) end @doc """ Formats a number by separating thousands with a separator. """ def format_number(number, separator \\ " ") do number |> to_charlist() |> Enum.reverse() |> Enum.chunk_every(3) |> Enum.intersperse(separator) |> List.flatten() |> Enum.reverse() |> to_string() end @doc """ Wraps the given EEx content with a EEx template. Templates can be bundled with a scenario or be a part of the system, `source` should be set to `scenario_name` for scenario-based templates and `:system` for templates bundled with the system. The templates that can be included, so called shared templates, are stored in `shared` folder (`scenarios/priv/notifications/{scenario_name}/shared`). Each shared template consists of two files - header and footer - which are wrapped around the given content. The filenames have `_header` and `_footer` appended, for `template_name = "email"` the two filenames are `email_header.eex` and `email_footer.eex`. System templates are stored in `apps/asset_map/templates/shared`. Via `context` you can pass additional parameters to the shared template (passed as assigns). The expected context depends on the template. ### Example usage ``` <% alias Toolbox.Notifications.TemplateHelper %> <% require TemplateHelper %> <%= TemplateHelper.include_template({"test", 1}, "email_rich", title: "Test email") do %>
This text is surrounded by the template...
<% end %> ``` """ defmacro include_template(source, template_name, context \\ [], macro_params) defmacro include_template(source, template_name, context, do: content) do case find_shared_templates(source, template_name) do {:ok, header, footer} -> quote bind_quoted: [header: header, footer: footer, content: content, context: context] do [ EEx.eval_file(header, assigns: context), content, EEx.eval_file(footer, assigns: context) ] end _ -> quote do unquote(content) end end end defp find_shared_templates(:system, template_name) do [ :code.priv_dir(:toolbox), "templates/shared" ] |> Path.join() |> get_shared_template(template_name) end defp find_shared_templates(scenario_name, template_name) when is_binary(scenario_name) do [ :code.priv_dir(Application.fetch_env!(:runbox, :scenario_app)), "notifications", scenario_name, "shared" ] |> Path.join() |> get_shared_template(template_name) end defp get_shared_template(base_path, template_name) do header = Path.join(base_path, "#{template_name}_header.eex") footer = Path.join(base_path, "#{template_name}_footer.eex") if File.exists?(header) && File.exists?(footer) do {:ok, header, footer} else :error end end end