defmodule Runbox.Notifications.TemplateHelper do
@moduledoc group: :utilities
@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!(:runbox, :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 Runbox.Notifications.TemplateHelper %>
<% require TemplateHelper %>
<%= TemplateHelper.include_template({"test", 1}, "email_rich", title: "Test email") do %>
<h1>Test Email</h1>
<p>This text is surrounded by the template...</p>
<% 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(:runbox),
"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