feat: Add email framework
This commit is contained in:
parent
47369a937a
commit
34241a8f83
21 changed files with 776 additions and 10 deletions
|
@ -1,4 +1,7 @@
|
|||
defmodule Content.Telemetry do
|
||||
@moduledoc """
|
||||
Collects metrics for the application and allows them to be transmitted using the Telemetry framework.
|
||||
"""
|
||||
use Supervisor
|
||||
import Telemetry.Metrics
|
||||
|
||||
|
|
|
@ -14,7 +14,8 @@ defmodule Content.MixProject do
|
|||
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
||||
start_permanent: Mix.env() == :prod,
|
||||
aliases: aliases(),
|
||||
deps: deps()
|
||||
deps: deps(),
|
||||
test_coverage: [tool: ExCoveralls],
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -37,6 +38,7 @@ defmodule Content.MixProject do
|
|||
# Type `mix help deps` for examples and options.
|
||||
defp deps do
|
||||
[
|
||||
{:excoveralls, "~> 0.10", only: [:dev, :test]},
|
||||
{:phoenix, "~> 1.5.3"},
|
||||
{:phoenix_ecto, "~> 4.0"},
|
||||
{:phoenix_html, "~> 2.11"},
|
||||
|
|
13
apps/core/lib/core/map_utils.ex
Normal file
13
apps/core/lib/core/map_utils.ex
Normal file
|
@ -0,0 +1,13 @@
|
|||
defmodule Core.MapUtils do
|
||||
def deep_merge(base, override) do
|
||||
Map.merge(base, override, &deep_value/3)
|
||||
end
|
||||
|
||||
defp deep_value(_key, base = %{}, override = %{}) do
|
||||
deep_merge(base, override)
|
||||
end
|
||||
|
||||
defp deep_value(_key, _base, override) do
|
||||
override
|
||||
end
|
||||
end
|
19
apps/core/lib/core_email.ex
Normal file
19
apps/core/lib/core_email.ex
Normal file
|
@ -0,0 +1,19 @@
|
|||
defmodule CoreEmail do
|
||||
@moduledoc """
|
||||
The core library for email in the application. The functions here can be composed in the application to send
|
||||
different emails.
|
||||
"""
|
||||
import Bamboo.Email
|
||||
use Bamboo.Phoenix, view: CoreWeb.CoreEmailView
|
||||
|
||||
def base_email() do
|
||||
new_email()
|
||||
|> put_html_layout({CoreWeb.CoreEmailView, "email.html"})
|
||||
|> put_text_layout({CoreWeb.CoreEmailView, "email.text"})
|
||||
|> from(sender())
|
||||
end
|
||||
|
||||
defp sender() do
|
||||
Application.get_env(:core, :email_from)
|
||||
end
|
||||
end
|
6
apps/core/lib/core_mailer.ex
Normal file
6
apps/core/lib/core_mailer.ex
Normal file
|
@ -0,0 +1,6 @@
|
|||
defmodule CoreMailer do
|
||||
@moduledoc """
|
||||
The base mailer for email for the application.
|
||||
"""
|
||||
use Bamboo.Mailer, otp_app: :core
|
||||
end
|
|
@ -38,6 +38,11 @@ defmodule CoreWeb.Router do
|
|||
end
|
||||
end
|
||||
|
||||
if Mix.env == :dev do
|
||||
# If using Phoenix
|
||||
forward "/sent_emails", Bamboo.SentEmailViewerPlug
|
||||
end
|
||||
|
||||
Application.get_env(:core, :router_forwards, [])
|
||||
|> Enum.map(fn router ->
|
||||
forward "/", router
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
defmodule CoreWeb.Telemetry do
|
||||
@moduledoc """
|
||||
Collects metrics for the application and allows them to be transmitted using the Telemetry framework.
|
||||
"""
|
||||
use Supervisor
|
||||
import Telemetry.Metrics
|
||||
|
||||
|
|
239
apps/core/lib/core_web/templates/core_email/email.html.eex
Normal file
239
apps/core/lib/core_web/templates/core_email/email.html.eex
Normal file
|
@ -0,0 +1,239 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
<head>
|
||||
<meta charset="utf-8"> <!-- utf-8 works for most cases -->
|
||||
<meta name="viewport" content="width=device-width"> <!-- Forcing initial-scale shouldn't be necessary -->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- Use the latest (edge) version of IE rendering engine -->
|
||||
<meta name="x-apple-disable-message-reformatting"> <!-- Disable auto-scale in iOS 10 Mail entirely -->
|
||||
<meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no"> <!-- Tell iOS not to automatically link certain text strings. -->
|
||||
<meta name="color-scheme" content="light">
|
||||
<meta name="supported-color-schemes" content="light">
|
||||
<title><%= assigns[:title] %></title> <!-- The title tag shows in email notifications, like Android 4.4. -->
|
||||
|
||||
<!-- What it does: Makes background images in 72ppi Outlook render at correct size. -->
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG/>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
|
||||
<!-- Web Font / @font-face : BEGIN -->
|
||||
<!-- NOTE: If web fonts are not required, lines 23 - 41 can be safely removed. -->
|
||||
|
||||
<!-- Desktop Outlook chokes on web font references and defaults to Times New Roman, so we force a safe fallback font. -->
|
||||
<!--[if mso]>
|
||||
<style>
|
||||
* {
|
||||
font-family: sans-serif !important;
|
||||
}
|
||||
</style>
|
||||
<![endif]-->
|
||||
|
||||
<!-- All other clients get the webfont reference; some will render the font and others will silently fail to the fallbacks. More on that here: http://stylecampaign.com/blog/2015/02/webfont-support-in-email/ -->
|
||||
<!--[if !mso]><!-->
|
||||
<!-- insert web font reference, eg: <link href='https://fonts.googleapis.com/css?family=Roboto:400,700' rel='stylesheet' type='text/css'> -->
|
||||
<!--<![endif]-->
|
||||
|
||||
<!-- Web Font / @font-face : END -->
|
||||
|
||||
<!-- CSS Reset : BEGIN -->
|
||||
<style>
|
||||
|
||||
/* What it does: Tells the email client that only light styles are provided but the client can transform them to dark. A duplicate of meta color-scheme meta tag above. */
|
||||
:root {
|
||||
color-scheme: light;
|
||||
supported-color-schemes: light;
|
||||
}
|
||||
|
||||
/* What it does: Remove spaces around the email design added by some email clients. */
|
||||
/* Beware: It can remove the padding / margin and add a background color to the compose a reply window. */
|
||||
html,
|
||||
body {
|
||||
margin: 0 auto !important;
|
||||
padding: 0 !important;
|
||||
height: 100% !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* What it does: Stops email clients resizing small text. */
|
||||
* {
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
/* What it does: Centers email on Android 4.4 */
|
||||
div[style*="margin: 16px 0"] {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/* What it does: forces Samsung Android mail clients to use the entire viewport */
|
||||
#MessageViewBody, #MessageWebViewDiv{
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* What it does: Stops Outlook from adding extra spacing to tables. */
|
||||
table,
|
||||
td {
|
||||
mso-table-lspace: 0pt !important;
|
||||
mso-table-rspace: 0pt !important;
|
||||
}
|
||||
|
||||
/* What it does: Fixes webkit padding issue. */
|
||||
table {
|
||||
border-spacing: 0 !important;
|
||||
border-collapse: collapse !important;
|
||||
table-layout: fixed !important;
|
||||
margin: 0 auto !important;
|
||||
}
|
||||
|
||||
/* What it does: Uses a better rendering method when resizing images in IE. */
|
||||
img {
|
||||
-ms-interpolation-mode:bicubic;
|
||||
}
|
||||
|
||||
/* What it does: Prevents Windows 10 Mail from underlining links despite inline CSS. Styles for underlined links should be inline. */
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* What it does: A work-around for email clients meddling in triggered links. */
|
||||
a[x-apple-data-detectors], /* iOS */
|
||||
.unstyle-auto-detected-links a,
|
||||
.aBn {
|
||||
border-bottom: 0 !important;
|
||||
cursor: default !important;
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
font-size: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
}
|
||||
|
||||
/* What it does: Prevents Gmail from displaying a download button on large, non-linked images. */
|
||||
.a6S {
|
||||
display: none !important;
|
||||
opacity: 0.01 !important;
|
||||
}
|
||||
|
||||
/* What it does: Prevents Gmail from changing the text color in conversation threads. */
|
||||
.im {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/* If the above doesn't work, add a .g-img class to any image in question. */
|
||||
img.g-img + div {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* What it does: Removes right gutter in Gmail iOS app: https://github.com/TedGoas/Cerberus/issues/89 */
|
||||
/* Create one of these media queries for each additional viewport size you'd like to fix */
|
||||
|
||||
/* iPhone 4, 4S, 5, 5S, 5C, and 5SE */
|
||||
@media only screen and (min-device-width: 320px) and (max-device-width: 374px) {
|
||||
u ~ div .email-container {
|
||||
min-width: 320px !important;
|
||||
}
|
||||
}
|
||||
/* iPhone 6, 6S, 7, 8, and X */
|
||||
@media only screen and (min-device-width: 375px) and (max-device-width: 413px) {
|
||||
u ~ div .email-container {
|
||||
min-width: 375px !important;
|
||||
}
|
||||
}
|
||||
/* iPhone 6+, 7+, and 8+ */
|
||||
@media only screen and (min-device-width: 414px) {
|
||||
u ~ div .email-container {
|
||||
min-width: 414px !important;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
<!-- CSS Reset : END -->
|
||||
|
||||
<!-- Progressive Enhancements : BEGIN -->
|
||||
<style>
|
||||
|
||||
/* What it does: Hover styles for buttons */
|
||||
.button-td,
|
||||
.button-a {
|
||||
transition: all 100ms ease-in;
|
||||
}
|
||||
.button-td-primary:hover,
|
||||
.button-a-primary:hover {
|
||||
background: #555555 !important;
|
||||
border-color: #555555 !important;
|
||||
}
|
||||
|
||||
/* Media Queries */
|
||||
@media screen and (max-width: 600px) {
|
||||
|
||||
/* What it does: Adjust typography on small screens to improve readability */
|
||||
.email-container p {
|
||||
font-size: 17px !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
<!-- Progressive Enhancements : END -->
|
||||
|
||||
</head>
|
||||
<!--
|
||||
The email background color (<%= effective_styles(:background)[:color] %>) is defined in three places:
|
||||
1. body tag: for most email clients
|
||||
2. center tag: for Gmail and Inbox mobile apps and web versions of Gmail, GSuite, Inbox, Yahoo, AOL, Libero, Comcast, freenet, Mail.ru, Orange.fr
|
||||
3. mso conditional: For Windows 10 Mail
|
||||
-->
|
||||
<body width="100%" style="margin: 0; padding: 0 !important; mso-line-height-rule: exactly; background-color: <%= effective_styles(:background)[:color] %>;">
|
||||
<center role="article" aria-roledescription="email" lang="en" style="width: 100%; background-color: <%= effective_styles(:background)[:color] %>;">
|
||||
<!--[if mso | IE]>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: <%= effective_styles(:background)[:color] %>;">
|
||||
<tr>
|
||||
<td>
|
||||
<![endif]-->
|
||||
|
||||
<%= if assigns[:preview_text] do %>
|
||||
<%= preview do %>
|
||||
<%= assigns[:preview_text] %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<!--
|
||||
Set the email width. Defined in two places:
|
||||
1. max-width for all clients except Desktop Windows Outlook, allowing the email to squish on narrow but never go wider than 600px.
|
||||
2. MSO tags for Desktop Windows Outlook enforce a 600px width.
|
||||
-->
|
||||
<div style="max-width: 600px; margin: 0 auto;" class="email-container">
|
||||
<!--[if mso]>
|
||||
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="600">
|
||||
<tr>
|
||||
<td>
|
||||
<![endif]-->
|
||||
|
||||
<!-- Email Body : BEGIN -->
|
||||
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="margin: auto;">
|
||||
<%= @inner_content %>
|
||||
</table>
|
||||
<!-- Email Body : END -->
|
||||
|
||||
<%= footer() %>
|
||||
|
||||
<!--[if mso]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
|
||||
<!--[if mso | IE]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
<%= @inner_content %>
|
318
apps/core/lib/core_web/views/core_email_view.ex
Normal file
318
apps/core/lib/core_web/views/core_email_view.ex
Normal file
|
@ -0,0 +1,318 @@
|
|||
defmodule CoreWeb.CoreEmailView do
|
||||
use Phoenix.View,
|
||||
root: "lib/core_web/templates",
|
||||
namespace: CoreWeb,
|
||||
pattern: "**/*"
|
||||
|
||||
import Phoenix.HTML, only: [sigil_E: 2]
|
||||
|
||||
def framework_styles do
|
||||
%{
|
||||
background: %{
|
||||
color: "#222222",
|
||||
},
|
||||
body: %{
|
||||
font_family: "sans-serif",
|
||||
font_size: "15px",
|
||||
line_height: "20px",
|
||||
text_color: "#555555",
|
||||
},
|
||||
button: %{
|
||||
border_radius: "4px",
|
||||
border: "1px solid #000000",
|
||||
background: "#222222",
|
||||
color: "#ffffff",
|
||||
font_family: "sans-serif",
|
||||
font_size: "15px",
|
||||
line_height: "15px",
|
||||
text_decoration: "none",
|
||||
padding: "13px 17px",
|
||||
display: "block",
|
||||
},
|
||||
column: %{
|
||||
background: "#FFFFFF",
|
||||
padding: "0 10px 40px 10px",
|
||||
},
|
||||
footer: %{
|
||||
padding: "20px",
|
||||
font_family: "sans-serif",
|
||||
font_size: "12px",
|
||||
line_height: "15px",
|
||||
text_align: "center",
|
||||
color: "#ffffff",
|
||||
},
|
||||
global: %{
|
||||
width: 600,
|
||||
},
|
||||
h1: %{
|
||||
margin: "0 0 10px 0",
|
||||
font_family: "sans-serif",
|
||||
font_size: "25px",
|
||||
line_height: "30px",
|
||||
color: "#333333",
|
||||
font_weight: "normal",
|
||||
},
|
||||
h2: %{
|
||||
margin: "0 0 10px 0",
|
||||
font_family: "sans-serif",
|
||||
font_size: "18px",
|
||||
line_height: "22px",
|
||||
color: "#333333",
|
||||
font_weight: "bold",
|
||||
},
|
||||
header: %{
|
||||
padding: "20px 0",
|
||||
text_align: "center",
|
||||
},
|
||||
hero_image: %{
|
||||
background: "#dddddd",
|
||||
display: "block",
|
||||
margin: "auto",
|
||||
},
|
||||
inner_column: %{
|
||||
padding: "10px 10px 0",
|
||||
},
|
||||
li: %{
|
||||
margin: "0 0 0 10px",
|
||||
},
|
||||
last_li: %{
|
||||
margin: "0 0 10px 30px",
|
||||
},
|
||||
spacer: %{
|
||||
height: "40",
|
||||
font_size: "0px",
|
||||
line_height: "0px",
|
||||
},
|
||||
ul: %{
|
||||
margin: "0 0 10px 0",
|
||||
padding: "0",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
def framework_styles(group) do
|
||||
Map.get(framework_styles(), group, %{})
|
||||
end
|
||||
|
||||
def application_styles(group) do
|
||||
styles = Application.get_env(:core, :email, %{}) |> Map.get(:styles, %{})
|
||||
|
||||
Map.get(styles, group, %{})
|
||||
end
|
||||
|
||||
def effective_styles(group, overrides \\ %{}) do
|
||||
group
|
||||
|> framework_styles()
|
||||
|> Core.MapUtils.deep_merge(application_styles(group))
|
||||
|> Map.merge(overrides)
|
||||
end
|
||||
|
||||
def preview(do: content) do
|
||||
~E"""
|
||||
<div style="max-height:0; overflow:hidden; mso-hide:all;" aria-hidden="true">
|
||||
<%= content %>
|
||||
</div>
|
||||
<!-- Visually Hidden Preheader Text : END -->
|
||||
|
||||
<!-- Create white space after the desired preview text so email clients don’t pull other distracting text into the inbox preview. Extend as necessary. -->
|
||||
<!-- Preview Text Spacing Hack : BEGIN -->
|
||||
<div style="display: none; font-size: 1px; line-height: 1px; max-height: 0px; max-width: 0px; opacity: 0; overflow: hidden; mso-hide: all; font-family: sans-serif;">
|
||||
‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌
|
||||
‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌
|
||||
‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌
|
||||
‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌
|
||||
‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌
|
||||
‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
def header(do: content) do
|
||||
~E"""
|
||||
<tr>
|
||||
<td style="<%= map_style(effective_styles(:header)) %>">
|
||||
<%= content %>
|
||||
</td>
|
||||
</tr>
|
||||
"""
|
||||
end
|
||||
|
||||
def spacer do
|
||||
style = effective_styles(:spacer)
|
||||
|
||||
~E"""
|
||||
<tr>
|
||||
<td aria-hidden="true" height="<%= style[:height] %>" style="<%= map_style(style) %>">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
"""
|
||||
end
|
||||
|
||||
def row(do: content) do
|
||||
~E"""
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<%= content %>
|
||||
</tr>
|
||||
</table>
|
||||
"""
|
||||
end
|
||||
|
||||
def col(n, opts, do: content) do
|
||||
{of, opts} = Keyword.pop!(opts, :of)
|
||||
width = n * 100.0 / of
|
||||
|
||||
~E"""
|
||||
<td valign="top" width="<%= width %>%" style="<%= map_style(effective_styles(:column)) %>">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="<%= map_style(effective_styles(:body)) %> <%= map_style(effective_styles(:inner_column)) %>">
|
||||
<%= content %>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
"""
|
||||
end
|
||||
|
||||
def hero_image(opts) do
|
||||
{src, _rest_opts} = Keyword.pop!(opts, :src)
|
||||
|
||||
~E"""
|
||||
<%= row do %>
|
||||
<%= col 1, of: 1 do %>
|
||||
<img
|
||||
src="<%= src %>"
|
||||
width="<%= effective_styles(:global)[:width] %>"
|
||||
height="auto"
|
||||
alt="alt_text"
|
||||
border="0"
|
||||
style="
|
||||
<%= map_style(effective_styles(:body)) %>
|
||||
width: 100%;
|
||||
max-width: <%= effective_styles(:global)[:width] %>;
|
||||
height: auto;
|
||||
<%= map_style(effective_styles(:hero_image)) %>
|
||||
"
|
||||
class="g-img"
|
||||
>
|
||||
<% end %>
|
||||
<% end %>
|
||||
"""
|
||||
end
|
||||
|
||||
def h1(do: content) do
|
||||
~E"""
|
||||
<h1 style="<%= map_style(effective_styles(:h1)) %>">
|
||||
<%= content %>
|
||||
</h1>
|
||||
"""
|
||||
end
|
||||
|
||||
def h2(do: content) do
|
||||
~E"""
|
||||
<h2 style="<%= map_style(effective_styles(:h2)) %>">
|
||||
<%= content %>
|
||||
</h2>
|
||||
"""
|
||||
end
|
||||
|
||||
def p(do: content) do
|
||||
~E"""
|
||||
<p style="<%= map_style(effective_styles(:body)) %>">
|
||||
<%= content %>
|
||||
</p>
|
||||
"""
|
||||
end
|
||||
|
||||
def button(opts, do: content) do
|
||||
{overrides, opts_without_style} = Keyword.pop(opts, :style, %{})
|
||||
{href, _rest_opts} = Keyword.pop!(opts_without_style, :href)
|
||||
|
||||
style = effective_styles(:button, overrides)
|
||||
cell_style = style |> Map.take([:border_radius, :background])
|
||||
|
||||
~E"""
|
||||
<%= wrapper do %>
|
||||
<td
|
||||
class="button-td button-td-primary"
|
||||
style="<%= map_style(cell_style) %>"
|
||||
>
|
||||
<a
|
||||
class="button-a button-a-primary"
|
||||
href="<%= href %>"
|
||||
style="<%= map_style(style) %>"
|
||||
>
|
||||
<%= content %>
|
||||
</a>
|
||||
</td>
|
||||
<% end %>
|
||||
"""
|
||||
end
|
||||
|
||||
def ul(opts) do
|
||||
{items, _rest_opts} = Keyword.pop!(opts, :items)
|
||||
|
||||
item_count = Enum.count(items)
|
||||
|
||||
item_tags =
|
||||
items
|
||||
|> Enum.with_index()
|
||||
|> Enum.map(fn {item, index} ->
|
||||
li_for_ul(index, item_count, item)
|
||||
end)
|
||||
|
||||
~E"""
|
||||
<ul style="<%= map_style(effective_styles(:ul)) %>">
|
||||
<%= item_tags %>
|
||||
</ul>
|
||||
"""
|
||||
end
|
||||
|
||||
def footer do
|
||||
~E"""
|
||||
<%= wrapper do %>
|
||||
<td style="<%= map_style(effective_styles(:footer)) %>">
|
||||
<%= I18n.t! "en", "email.company.name" %><br>
|
||||
<span class="unstyle-auto-detected-links">
|
||||
<%= I18n.t! "en", "email.company.address" %><br>
|
||||
<%= I18n.t! "en", "email.company.phone" %>
|
||||
</span>
|
||||
<br><br>
|
||||
</td>
|
||||
<% end %>
|
||||
"""
|
||||
end
|
||||
|
||||
defp li_for_ul(index, list_length, content) do
|
||||
last_styles = if index == list_length - 1, do: map_style(effective_styles(:last_li))
|
||||
~E"""
|
||||
<li style="<%= map_style(effective_styles(:li)) %>">
|
||||
<%= content %>
|
||||
</li>
|
||||
"""
|
||||
end
|
||||
|
||||
defp wrapper(do: content) do
|
||||
~E"""
|
||||
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;">
|
||||
<tr>
|
||||
<%= content %>
|
||||
</tr>
|
||||
</table>
|
||||
"""
|
||||
end
|
||||
|
||||
defp map_style(map) do
|
||||
map
|
||||
|> Enum.map(fn {key, value} ->
|
||||
new_key =
|
||||
key
|
||||
|> Atom.to_string()
|
||||
|> String.replace("_", "-")
|
||||
"#{new_key}: #{value};"
|
||||
end)
|
||||
|> Enum.join("\n")
|
||||
end
|
||||
end
|
18
apps/core/lib/i18n.ex
Normal file
18
apps/core/lib/i18n.ex
Normal file
|
@ -0,0 +1,18 @@
|
|||
defmodule I18n do
|
||||
@moduledoc """
|
||||
The internationalization and strings module. Keeps strings outside the codebase and allows them to
|
||||
be replaced on a per locale basis by editing yml files.
|
||||
"""
|
||||
use Linguist.Vocabulary
|
||||
|
||||
Path.join(["../../config/i18n", "*.yml"])
|
||||
|> Path.wildcard()
|
||||
|> Enum.each(fn path ->
|
||||
locale =
|
||||
path
|
||||
|> Path.basename()
|
||||
|> Path.rootname()
|
||||
|
||||
locale locale, path
|
||||
end)
|
||||
end
|
|
@ -5,12 +5,17 @@ defmodule Core.MixProject do
|
|||
[
|
||||
app: :core,
|
||||
version: "0.1.0",
|
||||
build_path: "../../_build",
|
||||
config_path: "../../config/config.exs",
|
||||
deps_path: "../../deps",
|
||||
lockfile: "../../mix.lock",
|
||||
elixir: "~> 1.7",
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
||||
start_permanent: Mix.env() == :prod,
|
||||
aliases: aliases(),
|
||||
deps: deps()
|
||||
deps: deps(),
|
||||
test_coverage: [tool: ExCoveralls],
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -33,9 +38,14 @@ defmodule Core.MixProject do
|
|||
# Type `mix help deps` for examples and options.
|
||||
defp deps do
|
||||
[
|
||||
{:bamboo, "~> 1.5"},
|
||||
{:excoveralls, "~> 0.10", only: [:dev, :test]},
|
||||
{:credo, "~> 1.4", only: [:dev, :test], runtime: false},
|
||||
{:ex_cldr, "~> 2.13.0"},
|
||||
{:phoenix, "~> 1.5.3"},
|
||||
{:phoenix_ecto, "~> 4.1"},
|
||||
{:ecto_sql, "~> 3.4"},
|
||||
{:linguist, "0.3.0"},
|
||||
{:postgrex, ">= 0.0.0"},
|
||||
{:phoenix_html, "~> 2.11"},
|
||||
{:phoenix_live_reload, "~> 1.2", only: :dev},
|
||||
|
@ -44,7 +54,7 @@ defmodule Core.MixProject do
|
|||
{:telemetry_poller, "~> 0.4"},
|
||||
{:gettext, "~> 0.11"},
|
||||
{:jason, "~> 1.0"},
|
||||
{:plug_cowboy, "~> 2.0"}
|
||||
{:plug_cowboy, "~> 2.0"},
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
%{
|
||||
"bamboo": {:hex, :bamboo, "1.5.0", "1926107d58adba6620450f254dfe8a3686637a291851fba125686fa8574842af", [:mix], [{:hackney, ">= 1.13.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "d5f3d04d154e80176fd685e2531e73870d8700679f14d25a567e448abce6298d"},
|
||||
"certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "3b3b5f36493004ac3455966991eaf6e768ce9884693d9968055aeeeb1e575040"},
|
||||
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"},
|
||||
"cowboy": {:hex, :cowboy, "2.8.0", "f3dc62e35797ecd9ac1b50db74611193c29815401e53bac9a5c0577bd7bc667d", [:rebar3], [{:cowlib, "~> 2.9.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "4643e4fba74ac96d4d152c75803de6fad0b3fa5df354c71afdd6cbeeb15fac8a"},
|
||||
"cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"},
|
||||
|
@ -8,8 +10,13 @@
|
|||
"ecto_sql": {:hex, :ecto_sql, "3.4.4", "d28bac2d420f708993baed522054870086fd45016a9d09bb2cd521b9c48d32ea", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "edb49af715dd72f213b66adfd0f668a43c17ed510b5d9ac7528569b23af57fe8"},
|
||||
"file_system": {:hex, :file_system, "0.2.8", "f632bd287927a1eed2b718f22af727c5aeaccc9a98d8c2bd7bff709e851dc986", [:mix], [], "hexpm", "97a3b6f8d63ef53bd0113070102db2ce05352ecf0d25390eb8d747c2bde98bca"},
|
||||
"gettext": {:hex, :gettext, "0.18.0", "406d6b9e0e3278162c2ae1de0a60270452c553536772167e2d701f028116f870", [:mix], [], "hexpm", "c3f850be6367ebe1a08616c2158affe4a23231c70391050bf359d5f92f66a571"},
|
||||
"hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "3bf0bebbd5d3092a3543b783bf065165fa5d3ad4b899b836810e513064134e18"},
|
||||
"idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a02c8a1c4fd601215bb0b0324c8a6986749f807ce35f25449ec9e69758708122"},
|
||||
"jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"},
|
||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"},
|
||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
||||
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
|
||||
"phoenix": {:hex, :phoenix, "1.5.3", "bfe0404e48ea03dfe17f141eff34e1e058a23f15f109885bbdcf62be303b49ff", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8e16febeb9640d8b33895a691a56481464b82836d338bb3a23125cd7b6157c25"},
|
||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.1.0", "a044d0756d0464c5a541b4a0bf4bcaf89bffcaf92468862408290682c73ae50d", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "c5e666a341ff104d0399d8f0e4ff094559b2fde13a5985d4cb5023b2c2ac558b"},
|
||||
"phoenix_html": {:hex, :phoenix_html, "2.14.2", "b8a3899a72050f3f48a36430da507dd99caf0ac2d06c77529b1646964f3d563e", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "58061c8dfd25da5df1ea0ca47c972f161beb6c875cd293917045b92ffe1bf617"},
|
||||
|
@ -22,7 +29,9 @@
|
|||
"plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"},
|
||||
"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"},
|
||||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
||||
"telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"},
|
||||
"telemetry_metrics": {:hex, :telemetry_metrics, "0.5.0", "1b796e74add83abf844e808564275dfb342bcc930b04c7577ab780e262b0d998", [:mix], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "31225e6ce7a37a421a0a96ec55244386aec1c190b22578bd245188a4a33298fd"},
|
||||
"telemetry_poller": {:hex, :telemetry_poller, "0.5.1", "21071cc2e536810bac5628b935521ff3e28f0303e770951158c73eaaa01e962a", [:rebar3], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4cab72069210bc6e7a080cec9afffad1b33370149ed5d379b81c7c5f0c663fd4"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm", "d48d002e15f5cc105a696cf2f1bbb3fc72b4b770a184d8420c8db20da2674b38"},
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
use Mix.Config
|
||||
|
||||
config :core, router_forwards: [Content.Router]
|
||||
config :core,
|
||||
router_forwards: [Content.Router],
|
||||
email_from: "example@example.org"
|
||||
|
||||
config :content,
|
||||
generators: [context_app: false]
|
||||
|
||||
config :content, Content.Endpoint,
|
||||
server: false
|
||||
config :content, Content.Endpoint, server: false
|
||||
|
||||
import_config "../apps/*/config/config.exs"
|
||||
|
||||
|
@ -18,6 +19,10 @@ config :logger, :console,
|
|||
# Use Jason for JSON parsing in Phoenix
|
||||
config :phoenix, :json_library, Jason
|
||||
|
||||
config :linguist, pluralization_key: :count
|
||||
|
||||
import_config "email_styles.exs"
|
||||
|
||||
# Import environment specific config. This must remain at the bottom
|
||||
# of this file so it overrides the configuration defined above.
|
||||
import_config "#{Mix.env()}.exs"
|
||||
|
|
|
@ -55,3 +55,5 @@ config :content, Content.Endpoint,
|
|||
~r"lib/content/templates/.*(eex)$"
|
||||
]
|
||||
]
|
||||
|
||||
config :core, CoreMailer, adapter: Bamboo.LocalAdapter
|
||||
|
|
85
config/email_styles.exs
Normal file
85
config/email_styles.exs
Normal file
|
@ -0,0 +1,85 @@
|
|||
use Mix.Config
|
||||
|
||||
config :core, :email, %{
|
||||
styles: %{
|
||||
background: %{
|
||||
color: "#222222",
|
||||
},
|
||||
body: %{
|
||||
font_family: "sans-serif",
|
||||
font_size: "15px",
|
||||
line_height: "20px",
|
||||
text_color: "#555555",
|
||||
},
|
||||
button: %{
|
||||
border_radius: "4px",
|
||||
border: "1px solid #000000",
|
||||
background: "#222222",
|
||||
color: "#ffffff",
|
||||
font_family: "sans-serif",
|
||||
font_size: "15px",
|
||||
line_height: "15px",
|
||||
text_decoration: "none",
|
||||
padding: "13px 17px",
|
||||
display: "block",
|
||||
},
|
||||
column: %{
|
||||
background: "#FFFFFF",
|
||||
padding: "0 10px 40px 10px",
|
||||
},
|
||||
footer: %{
|
||||
padding: "20px",
|
||||
font_family: "sans-serif",
|
||||
font_size: "12px",
|
||||
line_height: "15px",
|
||||
text_align: "center",
|
||||
color: "#ffffff",
|
||||
},
|
||||
global: %{
|
||||
width: 600,
|
||||
},
|
||||
h1: %{
|
||||
margin: "0 0 10px 0",
|
||||
font_family: "sans-serif",
|
||||
font_size: "25px",
|
||||
line_height: "30px",
|
||||
color: "#333333",
|
||||
font_weight: "normal",
|
||||
},
|
||||
h2: %{
|
||||
margin: "0 0 10px 0",
|
||||
font_family: "sans-serif",
|
||||
font_size: "18px",
|
||||
line_height: "22px",
|
||||
color: "#333333",
|
||||
font_weight: "bold",
|
||||
},
|
||||
header: %{
|
||||
padding: "20px 0",
|
||||
text_align: "center",
|
||||
},
|
||||
hero_image: %{
|
||||
background: "#dddddd",
|
||||
display: "block",
|
||||
margin: "auto",
|
||||
},
|
||||
inner_column: %{
|
||||
padding: "10px 10px 0",
|
||||
},
|
||||
li: %{
|
||||
margin: "0 0 0 10px",
|
||||
},
|
||||
last_li: %{
|
||||
margin: "0 0 10px 30px",
|
||||
},
|
||||
spacer: %{
|
||||
height: "40",
|
||||
font_size: "0px",
|
||||
line_height: "0px",
|
||||
},
|
||||
ul: %{
|
||||
margin: "0 0 10px 0",
|
||||
padding: "0",
|
||||
},
|
||||
}
|
||||
}
|
5
config/en.yml
Normal file
5
config/en.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
email:
|
||||
company:
|
||||
name: ""
|
||||
address: ""
|
||||
phone: ""
|
5
config/i18n/en.yml
Normal file
5
config/i18n/en.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
email:
|
||||
company:
|
||||
name: ""
|
||||
address: ""
|
||||
phone: ""
|
7
mix.exs
7
mix.exs
|
@ -4,10 +4,11 @@ defmodule Legendary.Mixfile do
|
|||
def project do
|
||||
[
|
||||
apps_path: "apps",
|
||||
build_embedded: Mix.env == :prod,
|
||||
start_permanent: Mix.env == :prod,
|
||||
build_embedded: Mix.env() == :prod,
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps(),
|
||||
aliases: aliases()
|
||||
aliases: aliases(),
|
||||
test_coverage: [tool: ExCoveralls],
|
||||
]
|
||||
end
|
||||
|
||||
|
|
17
mix.lock
17
mix.lock
|
@ -1,15 +1,28 @@
|
|||
%{
|
||||
"bamboo": {:hex, :bamboo, "1.5.0", "1926107d58adba6620450f254dfe8a3686637a291851fba125686fa8574842af", [:mix], [{:hackney, ">= 1.13.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "d5f3d04d154e80176fd685e2531e73870d8700679f14d25a567e448abce6298d"},
|
||||
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
|
||||
"certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "3b3b5f36493004ac3455966991eaf6e768ce9884693d9968055aeeeb1e575040"},
|
||||
"cldr_utils": {:hex, :cldr_utils, "2.9.1", "be714403abe1a7abed5ee4f7dd3823a9067f96ab4b0613a454177b51ca204236", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "6cba0a485f57feb773291ca1816469ddd887e22d73d9b12a1b207d82a67a4e71"},
|
||||
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"},
|
||||
"cowboy": {:hex, :cowboy, "2.8.0", "f3dc62e35797ecd9ac1b50db74611193c29815401e53bac9a5c0577bd7bc667d", [:rebar3], [{:cowlib, "~> 2.9.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "4643e4fba74ac96d4d152c75803de6fad0b3fa5df354c71afdd6cbeeb15fac8a"},
|
||||
"cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"},
|
||||
"credo": {:hex, :credo, "1.4.0", "92339d4cbadd1e88b5ee43d427b639b68a11071b6f73854e33638e30a0ea11f5", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1fd3b70dce216574ce3c18bdf510b57e7c4c85c2ec9cad4bff854abaf7e58658"},
|
||||
"db_connection": {:hex, :db_connection, "2.2.2", "3bbca41b199e1598245b716248964926303b5d4609ff065125ce98bcd368939e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "642af240d8a8affb93b4ba5a6fcd2bbcbdc327e1a524b825d383711536f8070c"},
|
||||
"decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"},
|
||||
"ecto": {:hex, :ecto, "3.4.5", "2bcd262f57b2c888b0bd7f7a28c8a48aa11dc1a2c6a858e45dd8f8426d504265", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8c6d1d4d524559e9b7a062f0498e2c206122552d63eacff0a6567ffe7a8e8691"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.4.4", "d28bac2d420f708993baed522054870086fd45016a9d09bb2cd521b9c48d32ea", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "edb49af715dd72f213b66adfd0f668a43c17ed510b5d9ac7528569b23af57fe8"},
|
||||
"ex_cldr": {:hex, :ex_cldr, "2.13.0", "742f14a4afcfea61a190d603d8e555d2c91d71e4e8fc2520d5dc35616969e225", [:mix], [{:cldr_utils, "~> 2.3", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.13", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "5e4cf3e945ee60156a3342e2a762f69036ffbe1f80520cc88592d68f12c5db55"},
|
||||
"excoveralls": {:hex, :excoveralls, "0.13.0", "4e1b7cc4e0351d8d16e9be21b0345a7e165798ee5319c7800b9138ce17e0b38e", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "fe2a56c8909564e2e6764765878d7d5e141f2af3bc8ff3b018a68ee2a218fced"},
|
||||
"file_system": {:hex, :file_system, "0.2.8", "f632bd287927a1eed2b718f22af727c5aeaccc9a98d8c2bd7bff709e851dc986", [:mix], [], "hexpm", "97a3b6f8d63ef53bd0113070102db2ce05352ecf0d25390eb8d747c2bde98bca"},
|
||||
"gettext": {:hex, :gettext, "0.18.0", "406d6b9e0e3278162c2ae1de0a60270452c553536772167e2d701f028116f870", [:mix], [], "hexpm", "c3f850be6367ebe1a08616c2158affe4a23231c70391050bf359d5f92f66a571"},
|
||||
"hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "3bf0bebbd5d3092a3543b783bf065165fa5d3ad4b899b836810e513064134e18"},
|
||||
"idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a02c8a1c4fd601215bb0b0324c8a6986749f807ce35f25449ec9e69758708122"},
|
||||
"jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"},
|
||||
"linguist": {:hex, :linguist, "0.3.0", "2984dfce6720d1212ddd7bba82496f92627a39aecd4d32c7016ec00393e1f925", [:mix], [{:ex_cldr, "~> 2.0", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.0", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "1923876545db22b63334c9d203ef56397a2946daa018117767b068f856be41e4"},
|
||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"},
|
||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
||||
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
|
||||
"phoenix": {:hex, :phoenix, "1.5.3", "bfe0404e48ea03dfe17f141eff34e1e058a23f15f109885bbdcf62be303b49ff", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8e16febeb9640d8b33895a691a56481464b82836d338bb3a23125cd7b6157c25"},
|
||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.1.0", "a044d0756d0464c5a541b4a0bf4bcaf89bffcaf92468862408290682c73ae50d", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "c5e666a341ff104d0399d8f0e4ff094559b2fde13a5985d4cb5023b2c2ac558b"},
|
||||
"phoenix_html": {:hex, :phoenix_html, "2.14.2", "b8a3899a72050f3f48a36430da507dd99caf0ac2d06c77529b1646964f3d563e", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "58061c8dfd25da5df1ea0ca47c972f161beb6c875cd293917045b92ffe1bf617"},
|
||||
|
@ -22,7 +35,11 @@
|
|||
"plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"},
|
||||
"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"},
|
||||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
||||
"telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"},
|
||||
"telemetry_metrics": {:hex, :telemetry_metrics, "0.5.0", "1b796e74add83abf844e808564275dfb342bcc930b04c7577ab780e262b0d998", [:mix], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "31225e6ce7a37a421a0a96ec55244386aec1c190b22578bd245188a4a33298fd"},
|
||||
"telemetry_poller": {:hex, :telemetry_poller, "0.5.1", "21071cc2e536810bac5628b935521ff3e28f0303e770951158c73eaaa01e962a", [:rebar3], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4cab72069210bc6e7a080cec9afffad1b33370149ed5d379b81c7c5f0c663fd4"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm", "d48d002e15f5cc105a696cf2f1bbb3fc72b4b770a184d8420c8db20da2674b38"},
|
||||
"yamerl": {:hex, :yamerl, "0.8.0", "8214cfe16bbabe5d1d6c14a14aea11c784b9a21903dd6a7c74f8ce180adae5c7", [:rebar3], [], "hexpm", "010634477bf9c208a0767dcca89116c2442cf0b5e87f9c870f85cd1c3e0c2aab"},
|
||||
"yaml_elixir": {:hex, :yaml_elixir, "2.4.0", "2f444abc3c994c902851fde56b6a9cb82895c291c05a0490a289035c2e62ae71", [:mix], [{:yamerl, "~> 0.7", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "4e25a6d5c873e393689c6f1062c5ec90f6cd1be2527b073178ae37eae4c78bee"},
|
||||
}
|
||||
|
|
|
@ -8,4 +8,4 @@ DIR_PATH=$(dirname $FULL_PATH)
|
|||
|
||||
$DIR_PATH/update
|
||||
|
||||
mix phx.server
|
||||
iex -S mix phx.server
|
||||
|
|
Loading…
Reference in a new issue