upgrade to phoenix 1.7
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@ -6,7 +6,8 @@ defmodule MemexWeb.EmailController do
|
||||
use MemexWeb, :controller
|
||||
alias Memex.Accounts.User
|
||||
|
||||
plug :put_layout, {MemexWeb.LayoutView, :email}
|
||||
plug :put_root_layout, html: {MemexWeb.Layouts, :email_html}
|
||||
plug :put_layout, false
|
||||
|
||||
@sample_assigns %{
|
||||
email: %{subject: "Example subject"},
|
||||
@ -18,6 +19,6 @@ defmodule MemexWeb.EmailController do
|
||||
Debug route used to preview emails
|
||||
"""
|
||||
def preview(conn, %{"id" => template}) do
|
||||
render(conn, "#{template |> to_string()}.html", @sample_assigns)
|
||||
render(conn, String.to_existing_atom(template), @sample_assigns)
|
||||
end
|
||||
end
|
||||
|
9
lib/memex_web/controllers/email_html.ex
Normal file
9
lib/memex_web/controllers/email_html.ex
Normal file
@ -0,0 +1,9 @@
|
||||
defmodule MemexWeb.EmailHTML do
|
||||
@moduledoc """
|
||||
Renders email templates
|
||||
"""
|
||||
|
||||
use MemexWeb, :html
|
||||
|
||||
embed_templates "email_html/*"
|
||||
end
|
@ -0,0 +1,23 @@
|
||||
<div style="display: flex; flex-direction: column; justify-content: center; align-items: center;">
|
||||
<span style="margin-bottom: 0.75em; font-size: 1.5em;">
|
||||
<%= dgettext("emails", "Hi %{email},", email: @user.email) %>
|
||||
</span>
|
||||
|
||||
<br />
|
||||
|
||||
<span style="margin-bottom: 1em; font-size: 1.25em;">
|
||||
<%= dgettext("emails", "Welcome to memEx") %>
|
||||
</span>
|
||||
|
||||
<br />
|
||||
|
||||
<%= dgettext("emails", "You can confirm your account by visiting the URL below:") %>
|
||||
|
||||
<br />
|
||||
|
||||
<a style="margin: 1em; color: rgb(161, 161, 170);" href={@url}><%= @url %></a>
|
||||
|
||||
<br />
|
||||
|
||||
<%= dgettext("emails", "If you didn't create an account at memEx, please ignore this.") %>
|
||||
</div>
|
@ -0,0 +1,12 @@
|
||||
|
||||
<%= dgettext("emails", "Hi %{email},", email: @user.email) %>
|
||||
|
||||
<%= dgettext("emails", "Welcome to memEx") %>
|
||||
|
||||
<%= dgettext("emails", "You can confirm your account by visiting the URL below:") %>
|
||||
|
||||
<%= @url %>
|
||||
|
||||
<%= dgettext("emails",
|
||||
"If you didn't create an account at %{url}, please ignore this.",
|
||||
url: ~p"/") %>
|
@ -0,0 +1,17 @@
|
||||
<div style="display: flex; flex-direction: column; justify-content: center; align-items: center;">
|
||||
<span style="margin-bottom: 0.5em; font-size: 1.5em;">
|
||||
<%= dgettext("emails", "Hi %{email},", email: @user.email) %>
|
||||
</span>
|
||||
|
||||
<br />
|
||||
|
||||
<%= dgettext("emails", "You can reset your password by visiting the URL below:") %>
|
||||
|
||||
<br />
|
||||
|
||||
<a style="margin: 1em; color: rgb(161, 161, 170);" href={@url}><%= @url %></a>
|
||||
|
||||
<br />
|
||||
|
||||
<%= dgettext("emails", "If you didn't request this change from memEx, please ignore this.") %>
|
||||
</div>
|
@ -0,0 +1,10 @@
|
||||
|
||||
<%= dgettext("emails", "Hi %{email},", email: @user.email) %>
|
||||
|
||||
<%= dgettext("emails", "You can reset your password by visiting the URL below:") %>
|
||||
|
||||
<%= @url %>
|
||||
|
||||
<%= dgettext("emails",
|
||||
"If you didn't request this change from %{url}, please ignore this.",
|
||||
url: ~p"/") %>
|
@ -0,0 +1,20 @@
|
||||
<div style="display: flex; flex-direction: column; justify-content: center; align-items: center;">
|
||||
<span style="margin-bottom: 0.5em; font-size: 1.5em;">
|
||||
<%= dgettext("emails", "Hi %{email},", email: @user.email) %>
|
||||
</span>
|
||||
|
||||
<br />
|
||||
|
||||
<%= dgettext("emails", "You can change your email by visiting the URL below:") %>
|
||||
|
||||
<br />
|
||||
|
||||
<a style="margin: 1em; color: rgb(161, 161, 170);" href={@url}><%= @url %></a>
|
||||
|
||||
<br />
|
||||
|
||||
<%= dgettext(
|
||||
"emails",
|
||||
"If you didn't request this change from memEx, please ignore this."
|
||||
) %>
|
||||
</div>
|
@ -0,0 +1,10 @@
|
||||
|
||||
<%= dgettext("emails", "Hi %{email},", email: @user.email) %>
|
||||
|
||||
<%= dgettext("emails", "You can change your email by visiting the URL below:") %>
|
||||
|
||||
<%= @url %>
|
||||
|
||||
<%= dgettext("emails",
|
||||
"If you didn't request this change from %{url}, please ignore this.",
|
||||
url: ~p"/") %>
|
16
lib/memex_web/controllers/error_html.ex
Normal file
16
lib/memex_web/controllers/error_html.ex
Normal file
@ -0,0 +1,16 @@
|
||||
defmodule MemexWeb.ErrorHTML do
|
||||
use MemexWeb, :html
|
||||
|
||||
embed_templates "error_html/*"
|
||||
|
||||
def render(template, _assigns) do
|
||||
error_string =
|
||||
case template do
|
||||
"404.html" -> dgettext("errors", "not found")
|
||||
"401.html" -> dgettext("errors", "unauthorized")
|
||||
_other_path -> dgettext("errors", "internal server error")
|
||||
end
|
||||
|
||||
error(%{error_string: error_string})
|
||||
end
|
||||
end
|
33
lib/memex_web/controllers/error_html/error.html.heex
Normal file
33
lib/memex_web/controllers/error_html/error.html.heex
Normal file
@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="m-0 p-0 w-full h-full">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>
|
||||
<%= dgettext("errors", "Error") %> | <%= gettext("memEx") %>
|
||||
</title>
|
||||
<link rel="stylesheet" href="/css/app.css" />
|
||||
<script defer type="text/javascript" src="/js/app.js">
|
||||
</script>
|
||||
</head>
|
||||
<body class="pb-8 m-0 p-0 w-full h-full bg-primary-800 text-primary-400 subpixel-antialiased">
|
||||
<header>
|
||||
<.topbar current_user={assigns[:current_user]} />
|
||||
</header>
|
||||
|
||||
<div class="pb-8 w-full flex flex-col justify-center items-center text-center">
|
||||
<div class="p-8 sm:p-16 w-full flex flex-col justify-center items-center space-y-4 max-w-3xl">
|
||||
<h1 class="title text-primary-400 text-3xl">
|
||||
<%= @error_string %>
|
||||
</h1>
|
||||
|
||||
<hr class="w-full hr" />
|
||||
|
||||
<.link href={~p"/"} class="link title text-primary-400 text-lg">
|
||||
<%= dgettext("errors", "go back home") %>
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
14
lib/memex_web/controllers/error_json.ex
Normal file
14
lib/memex_web/controllers/error_json.ex
Normal file
@ -0,0 +1,14 @@
|
||||
defmodule MemexWeb.ErrorJSON do
|
||||
import MemexWeb.Gettext
|
||||
|
||||
def render(template, _assigns) do
|
||||
error_string =
|
||||
case template do
|
||||
"404.json" -> dgettext("errors", "not found")
|
||||
"401.json" -> dgettext("errors", "unauthorized")
|
||||
_other_path -> dgettext("errors", "internal server error")
|
||||
end
|
||||
|
||||
%{errors: %{detail: error_string}}
|
||||
end
|
||||
end
|
@ -1,11 +0,0 @@
|
||||
defmodule MemexWeb.HomeController do
|
||||
@moduledoc """
|
||||
Controller for home page
|
||||
"""
|
||||
|
||||
use MemexWeb, :controller
|
||||
|
||||
def index(conn, _params) do
|
||||
render(conn, "index.html")
|
||||
end
|
||||
end
|
5
lib/memex_web/controllers/home_html.ex
Normal file
5
lib/memex_web/controllers/home_html.ex
Normal file
@ -0,0 +1,5 @@
|
||||
defmodule MemexWeb.HomeHTML do
|
||||
use MemexWeb, :html
|
||||
|
||||
embed_templates "home_html/*"
|
||||
end
|
@ -3,12 +3,11 @@ defmodule MemexWeb.UserAuth do
|
||||
Functions for user session and authentication
|
||||
"""
|
||||
|
||||
use MemexWeb, :verified_routes
|
||||
import Plug.Conn
|
||||
import Phoenix.Controller
|
||||
import MemexWeb.Gettext
|
||||
alias Memex.{Accounts, Accounts.User}
|
||||
alias MemexWeb.HomeLive
|
||||
alias MemexWeb.Router.Helpers, as: Routes
|
||||
|
||||
# Make the remember me cookie valid for 60 days.
|
||||
# If you want bump or reduce this value, also change
|
||||
@ -39,7 +38,7 @@ defmodule MemexWeb.UserAuth do
|
||||
dgettext("errors", "You must confirm your account and log in to access this page.")
|
||||
)
|
||||
|> maybe_store_return_to()
|
||||
|> redirect(to: Routes.user_session_path(conn, :new))
|
||||
|> redirect(to: ~p"/users/log_in")
|
||||
|> halt()
|
||||
end
|
||||
|
||||
@ -49,8 +48,7 @@ defmodule MemexWeb.UserAuth do
|
||||
|
||||
conn
|
||||
|> renew_session()
|
||||
|> put_session(:user_token, token)
|
||||
|> put_session(:live_socket_id, "users_sessions:#{Base.url_encode64(token)}")
|
||||
|> put_token_in_session(token)
|
||||
|> maybe_write_remember_me_cookie(token, params)
|
||||
|> redirect(to: user_return_to || signed_in_path(conn))
|
||||
end
|
||||
@ -96,7 +94,7 @@ defmodule MemexWeb.UserAuth do
|
||||
"""
|
||||
def log_out_user(conn) do
|
||||
user_token = get_session(conn, :user_token)
|
||||
user_token && Accounts.delete_session_token(user_token)
|
||||
user_token && Accounts.delete_user_session_token(user_token)
|
||||
|
||||
if live_socket_id = get_session(conn, :live_socket_id) do
|
||||
MemexWeb.Endpoint.broadcast(live_socket_id, "disconnect", %{})
|
||||
@ -105,7 +103,7 @@ defmodule MemexWeb.UserAuth do
|
||||
conn
|
||||
|> renew_session()
|
||||
|> delete_resp_cookie(@remember_me_cookie)
|
||||
|> redirect(to: "/")
|
||||
|> redirect(to: ~p"/")
|
||||
end
|
||||
|
||||
@doc """
|
||||
@ -119,19 +117,110 @@ defmodule MemexWeb.UserAuth do
|
||||
end
|
||||
|
||||
defp ensure_user_token(conn) do
|
||||
if user_token = get_session(conn, :user_token) do
|
||||
{user_token, conn}
|
||||
if token = get_session(conn, :user_token) do
|
||||
{token, conn}
|
||||
else
|
||||
conn = fetch_cookies(conn, signed: [@remember_me_cookie])
|
||||
|
||||
if user_token = conn.cookies[@remember_me_cookie] do
|
||||
{user_token, put_session(conn, :user_token, user_token)}
|
||||
if token = conn.cookies[@remember_me_cookie] do
|
||||
{token, put_token_in_session(conn, token)}
|
||||
else
|
||||
{nil, conn}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Handles mounting and authenticating the current_user in LiveViews.
|
||||
|
||||
## `on_mount` arguments
|
||||
|
||||
* `:mount_current_user` - Assigns current_user
|
||||
to socket assigns based on user_token, or nil if
|
||||
there's no user_token or no matching user.
|
||||
|
||||
* `:ensure_authenticated` - Authenticates the user from the session,
|
||||
and assigns the current_user to socket assigns based
|
||||
on user_token.
|
||||
Redirects to login page if there's no logged user.
|
||||
|
||||
* `:redirect_if_user_is_authenticated` - Authenticates the user from the session.
|
||||
Redirects to signed_in_path if there's a logged user.
|
||||
|
||||
## Examples
|
||||
|
||||
Use the `on_mount` lifecycle macro in LiveViews to mount or authenticate
|
||||
the current_user:
|
||||
|
||||
defmodule MemexWeb.PageLive do
|
||||
use MemexWeb, :live_view
|
||||
|
||||
on_mount {MemexWeb.UserAuth, :mount_current_user}
|
||||
...
|
||||
end
|
||||
|
||||
Or use the `live_session` of your router to invoke the on_mount callback:
|
||||
|
||||
live_session :authenticated, on_mount: [{MemexWeb.UserAuth, :ensure_authenticated}] do
|
||||
live "/profile", ProfileLive, :index
|
||||
end
|
||||
"""
|
||||
def on_mount(:mount_current_user, _params, session, socket) do
|
||||
{:cont, mount_current_user(session, socket)}
|
||||
end
|
||||
|
||||
def on_mount(:ensure_authenticated, _params, session, socket) do
|
||||
socket = mount_current_user(session, socket)
|
||||
|
||||
if socket.assigns.current_user do
|
||||
{:cont, socket}
|
||||
else
|
||||
error_flash = dgettext("errors", "You must log in to access this page.")
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> Phoenix.LiveView.put_flash(:error, error_flash)
|
||||
|> Phoenix.LiveView.redirect(to: ~p"/users/log_in")
|
||||
|
||||
{:halt, socket}
|
||||
end
|
||||
end
|
||||
|
||||
def on_mount(:ensure_admin, _params, session, socket) do
|
||||
socket = mount_current_user(session, socket)
|
||||
|
||||
if socket.assigns.current_user && socket.assigns.current_user.role == :admin do
|
||||
{:cont, socket}
|
||||
else
|
||||
error_flash = dgettext("errors", "You must log in as an administrator to access this page.")
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> Phoenix.LiveView.put_flash(:error, error_flash)
|
||||
|> Phoenix.LiveView.redirect(to: ~p"/users/log_in")
|
||||
|
||||
{:halt, socket}
|
||||
end
|
||||
end
|
||||
|
||||
def on_mount(:redirect_if_user_is_authenticated, _params, session, socket) do
|
||||
socket = mount_current_user(session, socket)
|
||||
|
||||
if socket.assigns.current_user do
|
||||
{:halt, Phoenix.LiveView.redirect(socket, to: signed_in_path(socket))}
|
||||
else
|
||||
{:cont, socket}
|
||||
end
|
||||
end
|
||||
|
||||
defp mount_current_user(session, socket) do
|
||||
Phoenix.Component.assign_new(socket, :current_user, fn ->
|
||||
if user_token = session["user_token"] do
|
||||
Accounts.get_user_by_session_token(user_token)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Used for routes that require the user to not be authenticated.
|
||||
"""
|
||||
@ -161,7 +250,7 @@ defmodule MemexWeb.UserAuth do
|
||||
dgettext("errors", "You must confirm your account and log in to access this page.")
|
||||
)
|
||||
|> maybe_store_return_to()
|
||||
|> redirect(to: Routes.user_session_path(conn, :new))
|
||||
|> redirect(to: ~p"/users/log_in")
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
@ -176,16 +265,34 @@ defmodule MemexWeb.UserAuth do
|
||||
conn
|
||||
|> put_flash(:error, dgettext("errors", "You are not authorized to view this page."))
|
||||
|> maybe_store_return_to()
|
||||
|> redirect(to: Routes.live_path(conn, HomeLive))
|
||||
|> redirect(to: ~p"/")
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
||||
def put_user_locale(%{assigns: %{current_user: %{locale: locale}}} = conn, _opts) do
|
||||
default = Application.fetch_env!(:gettext, :default_locale)
|
||||
Gettext.put_locale(locale || default)
|
||||
conn |> put_session(:locale, locale || default)
|
||||
end
|
||||
|
||||
def put_user_locale(conn, _opts) do
|
||||
default = Application.fetch_env!(:gettext, :default_locale)
|
||||
Gettext.put_locale(default)
|
||||
conn |> put_session(:locale, default)
|
||||
end
|
||||
|
||||
defp put_token_in_session(conn, token) do
|
||||
conn
|
||||
|> put_session(:user_token, token)
|
||||
|> put_session(:live_socket_id, "users_sessions:#{Base.url_encode64(token)}")
|
||||
end
|
||||
|
||||
defp maybe_store_return_to(%{method: "GET"} = conn) do
|
||||
put_session(conn, :user_return_to, current_path(conn))
|
||||
end
|
||||
|
||||
defp maybe_store_return_to(conn), do: conn
|
||||
|
||||
defp signed_in_path(_conn), do: "/"
|
||||
defp signed_in_path(_conn), do: ~p"/"
|
||||
end
|
||||
|
@ -5,14 +5,14 @@ defmodule MemexWeb.UserConfirmationController do
|
||||
alias Memex.Accounts
|
||||
|
||||
def new(conn, _params) do
|
||||
render(conn, "new.html", page_title: gettext("Confirm your account"))
|
||||
render(conn, :new, page_title: gettext("Confirm your account"))
|
||||
end
|
||||
|
||||
def create(conn, %{"user" => %{"email" => email}}) do
|
||||
if user = Accounts.get_user_by_email(email) do
|
||||
Accounts.deliver_user_confirmation_instructions(
|
||||
user,
|
||||
&Routes.user_confirmation_url(conn, :confirm, &1)
|
||||
fn token -> url(MemexWeb.Endpoint, ~p"/users/confirm/#{token}") end
|
||||
)
|
||||
end
|
||||
|
||||
|
6
lib/memex_web/controllers/user_confirmation_html.ex
Normal file
6
lib/memex_web/controllers/user_confirmation_html.ex
Normal file
@ -0,0 +1,6 @@
|
||||
defmodule MemexWeb.UserConfirmationHTML do
|
||||
use MemexWeb, :html
|
||||
alias Memex.Accounts
|
||||
|
||||
embed_templates "user_confirmation_html/*"
|
||||
end
|
@ -0,0 +1,31 @@
|
||||
<div class="mx-auto pb-8 max-w-2xl flex flex-col justify-center items-center space-y-4">
|
||||
<h1 class="title text-primary-400 text-xl">
|
||||
<%= dgettext("actions", "Resend confirmation instructions") %>
|
||||
</h1>
|
||||
|
||||
<.form
|
||||
:let={f}
|
||||
for={%{}}
|
||||
as={:user}
|
||||
action={~p"/users/confirm"}
|
||||
class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
|
||||
>
|
||||
<%= label(f, :email, gettext("Email"), class: "title text-lg text-primary-400") %>
|
||||
<%= email_input(f, :email, required: true, class: "input input-primary col-span-2") %>
|
||||
|
||||
<%= submit(dgettext("actions", "Resend confirmation instructions"),
|
||||
class: "mx-auto btn btn-primary col-span-3"
|
||||
) %>
|
||||
</.form>
|
||||
|
||||
<hr class="hr" />
|
||||
|
||||
<div class="flex flex-row justify-center items-center space-x-4">
|
||||
<.link :if={Accounts.allow_registration?()} href={~p"/users/register"} class="btn btn-primary">
|
||||
<%= dgettext("actions", "register") %>
|
||||
</.link>
|
||||
<.link href={~p"/users/log_in"} class="btn btn-primary">
|
||||
<%= dgettext("actions", "log in") %>
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
@ -2,7 +2,6 @@ defmodule MemexWeb.UserRegistrationController do
|
||||
use MemexWeb, :controller
|
||||
import MemexWeb.Gettext
|
||||
alias Memex.{Accounts, Accounts.Invites}
|
||||
alias MemexWeb.HomeLive
|
||||
|
||||
def new(conn, %{"invite" => invite_token}) do
|
||||
if Invites.valid_invite_token?(invite_token) do
|
||||
@ -10,7 +9,7 @@ defmodule MemexWeb.UserRegistrationController do
|
||||
else
|
||||
conn
|
||||
|> put_flash(:error, dgettext("errors", "Sorry, this invite was not found or expired"))
|
||||
|> redirect(to: Routes.live_path(Endpoint, HomeLive))
|
||||
|> redirect(to: ~p"/")
|
||||
end
|
||||
end
|
||||
|
||||
@ -20,13 +19,13 @@ defmodule MemexWeb.UserRegistrationController do
|
||||
else
|
||||
conn
|
||||
|> put_flash(:error, dgettext("errors", "Sorry, public registration is disabled"))
|
||||
|> redirect(to: Routes.live_path(Endpoint, HomeLive))
|
||||
|> redirect(to: ~p"/")
|
||||
end
|
||||
end
|
||||
|
||||
# renders new user registration page
|
||||
defp render_new(conn, invite_token \\ nil) do
|
||||
render(conn, "new.html",
|
||||
render(conn, :new,
|
||||
changeset: Accounts.change_user_registration(),
|
||||
invite_token: invite_token,
|
||||
page_title: gettext("register")
|
||||
@ -39,7 +38,7 @@ defmodule MemexWeb.UserRegistrationController do
|
||||
else
|
||||
conn
|
||||
|> put_flash(:error, dgettext("errors", "Sorry, this invite was not found or expired"))
|
||||
|> redirect(to: Routes.live_path(Endpoint, HomeLive))
|
||||
|> redirect(to: ~p"/")
|
||||
end
|
||||
end
|
||||
|
||||
@ -49,7 +48,7 @@ defmodule MemexWeb.UserRegistrationController do
|
||||
else
|
||||
conn
|
||||
|> put_flash(:error, dgettext("errors", "Sorry, public registration is disabled"))
|
||||
|> redirect(to: Routes.live_path(Endpoint, HomeLive))
|
||||
|> redirect(to: ~p"/")
|
||||
end
|
||||
end
|
||||
|
||||
@ -58,17 +57,17 @@ defmodule MemexWeb.UserRegistrationController do
|
||||
{:ok, user} ->
|
||||
Accounts.deliver_user_confirmation_instructions(
|
||||
user,
|
||||
&Routes.user_confirmation_url(conn, :confirm, &1)
|
||||
fn token -> url(MemexWeb.Endpoint, ~p"/users/confirm/#{token}") end
|
||||
)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, dgettext("prompts", "please check your email to verify your account"))
|
||||
|> redirect(to: Routes.user_session_path(Endpoint, :new))
|
||||
|> redirect(to: ~p"/users/log_in")
|
||||
|
||||
{:error, :invalid_token} ->
|
||||
conn
|
||||
|> put_flash(:error, dgettext("errors", "sorry, this invite was not found or expired"))
|
||||
|> redirect(to: Routes.live_path(Endpoint, HomeLive))
|
||||
|> redirect(to: ~p"/")
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
conn |> render("new.html", changeset: changeset, invite_token: invite_token)
|
||||
|
5
lib/memex_web/controllers/user_registration_html.ex
Normal file
5
lib/memex_web/controllers/user_registration_html.ex
Normal file
@ -0,0 +1,5 @@
|
||||
defmodule MemexWeb.UserRegistrationHTML do
|
||||
use MemexWeb, :html
|
||||
|
||||
embed_templates "user_registration_html/*"
|
||||
end
|
@ -0,0 +1,50 @@
|
||||
<div class="mx-auto pb-8 max-w-2xl flex flex-col justify-center items-center space-y-4">
|
||||
<h1 class="title text-primary-400 text-xl">
|
||||
<%= dgettext("actions", "register") %>
|
||||
</h1>
|
||||
|
||||
<.form
|
||||
:let={f}
|
||||
for={@changeset}
|
||||
action={~p"/users/register"}
|
||||
class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
|
||||
>
|
||||
<p :if={@changeset.action && not @changeset.valid?()} class="alert alert-danger col-span-3">
|
||||
<%= dgettext("errors", "oops, something went wrong! please check the errors below.") %>
|
||||
</p>
|
||||
|
||||
<%= if @invite_token do %>
|
||||
<%= hidden_input(f, :invite_token, value: @invite_token) %>
|
||||
<% end %>
|
||||
|
||||
<%= label(f, :email, gettext("email"), class: "title text-lg text-primary-400") %>
|
||||
<%= email_input(f, :email, required: true, class: "input input-primary col-span-2") %>
|
||||
<%= error_tag(f, :email, "col-span-3") %>
|
||||
|
||||
<%= label(f, :password, gettext("password"), class: "title text-lg text-primary-400") %>
|
||||
<%= password_input(f, :password, required: true, class: "input input-primary col-span-2") %>
|
||||
<%= error_tag(f, :password, "col-span-3") %>
|
||||
|
||||
<%= label(f, :locale, gettext("language"), class: "title text-lg text-primary-400") %>
|
||||
<%= select(
|
||||
f,
|
||||
:locale,
|
||||
[{gettext("english"), "en_US"}],
|
||||
class: "input input-primary col-span-2"
|
||||
) %>
|
||||
<%= error_tag(f, :locale) %>
|
||||
|
||||
<%= submit(dgettext("actions", "register"), class: "mx-auto btn btn-primary col-span-3") %>
|
||||
</.form>
|
||||
|
||||
<hr class="hr" />
|
||||
|
||||
<div class="flex flex-row justify-center items-center space-x-4">
|
||||
<.link href={~p"/users/log_in"} class="btn btn-primary">
|
||||
<%= dgettext("actions", "log in") %>
|
||||
</.link>
|
||||
<.link href={~p"/users/reset_password"} class="btn btn-primary">
|
||||
<%= dgettext("actions", "forgot your password?") %>
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
@ -6,14 +6,14 @@ defmodule MemexWeb.UserResetPasswordController do
|
||||
plug :get_user_by_reset_password_token when action in [:edit, :update]
|
||||
|
||||
def new(conn, _params) do
|
||||
render(conn, "new.html", page_title: gettext("forgot your password?"))
|
||||
render(conn, :new, page_title: gettext("forgot your password?"))
|
||||
end
|
||||
|
||||
def create(conn, %{"user" => %{"email" => email}}) do
|
||||
if user = Accounts.get_user_by_email(email) do
|
||||
Accounts.deliver_user_reset_password_instructions(
|
||||
user,
|
||||
&Routes.user_reset_password_url(conn, :edit, &1)
|
||||
fn token -> url(MemexWeb.Endpoint, ~p"/users/reset_password/#{token}") end
|
||||
)
|
||||
end
|
||||
|
||||
@ -31,7 +31,7 @@ defmodule MemexWeb.UserResetPasswordController do
|
||||
end
|
||||
|
||||
def edit(conn, _params) do
|
||||
render(conn, "edit.html",
|
||||
render(conn, :edit,
|
||||
changeset: Accounts.change_user_password(conn.assigns.user),
|
||||
page_title: gettext("Reset your password")
|
||||
)
|
||||
@ -44,10 +44,10 @@ defmodule MemexWeb.UserResetPasswordController do
|
||||
{:ok, _} ->
|
||||
conn
|
||||
|> put_flash(:info, dgettext("prompts", "Password reset successfully."))
|
||||
|> redirect(to: Routes.user_session_path(conn, :new))
|
||||
|> redirect(to: ~p"/users/log_in")
|
||||
|
||||
{:error, changeset} ->
|
||||
render(conn, "edit.html", changeset: changeset)
|
||||
render(conn, :edit, changeset: changeset)
|
||||
end
|
||||
end
|
||||
|
||||
|
6
lib/memex_web/controllers/user_reset_password_html.ex
Normal file
6
lib/memex_web/controllers/user_reset_password_html.ex
Normal file
@ -0,0 +1,6 @@
|
||||
defmodule MemexWeb.UserResetPasswordHTML do
|
||||
use MemexWeb, :html
|
||||
alias Memex.Accounts
|
||||
|
||||
embed_templates "user_reset_password_html/*"
|
||||
end
|
@ -0,0 +1,44 @@
|
||||
<div class="mx-auto pb-8 max-w-2xl flex flex-col justify-center items-center space-y-4">
|
||||
<h1 class="title text-primary-400 text-xl">
|
||||
<%= dgettext("actions", "Reset password") %>
|
||||
</h1>
|
||||
|
||||
<.form
|
||||
:let={f}
|
||||
for={@changeset}
|
||||
action={~p"/users/reset_password/#{@token}"}
|
||||
class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
|
||||
>
|
||||
<p :if={@changeset.action && not @changeset.valid?()} class="alert alert-danger col-span-3">
|
||||
<%= dgettext("errors", "oops, something went wrong! please check the errors below.") %>
|
||||
</p>
|
||||
|
||||
<%= label(f, :password, gettext("new password"), class: "title text-lg text-primary-400") %>
|
||||
<%= password_input(f, :password, required: true, class: "input input-primary col-span-2") %>
|
||||
<%= error_tag(f, :password, "col-span-3") %>
|
||||
|
||||
<%= label(f, :password_confirmation, gettext("confirm new password"),
|
||||
class: "title text-lg text-primary-400"
|
||||
) %>
|
||||
<%= password_input(f, :password_confirmation,
|
||||
required: true,
|
||||
class: "input input-primary col-span-2"
|
||||
) %>
|
||||
<%= error_tag(f, :password_confirmation, "col-span-3") %>
|
||||
|
||||
<%= submit(dgettext("actions", "Reset password"),
|
||||
class: "mx-auto btn btn-primary col-span-3"
|
||||
) %>
|
||||
</.form>
|
||||
|
||||
<hr class="hr" />
|
||||
|
||||
<div class="flex flex-row justify-center items-center space-x-4">
|
||||
<.link :if={Accounts.allow_registration?()} href={~p"/users/register"} class="btn btn-primary">
|
||||
<%= dgettext("actions", "register") %>
|
||||
</.link>
|
||||
<.link href={~p"/users/log_in"} class="btn btn-primary">
|
||||
<%= dgettext("actions", "log in") %>
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,31 @@
|
||||
<div class="mx-auto pb-8 max-w-2xl flex flex-col justify-center items-center space-y-4">
|
||||
<h1 class="title text-primary-400 text-xl">
|
||||
<%= dgettext("actions", "forgot your password?") %>
|
||||
</h1>
|
||||
|
||||
<.form
|
||||
:let={f}
|
||||
for={%{}}
|
||||
as={:user}
|
||||
action={~p"/users/reset_password"}
|
||||
class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
|
||||
>
|
||||
<%= label(f, :email, gettext("email"), class: "title text-lg text-primary-400") %>
|
||||
<%= email_input(f, :email, required: true, class: "input input-primary col-span-2") %>
|
||||
|
||||
<%= submit(dgettext("actions", "send instructions to reset password"),
|
||||
class: "mx-auto btn btn-primary col-span-3"
|
||||
) %>
|
||||
</.form>
|
||||
|
||||
<hr class="hr" />
|
||||
|
||||
<div class="flex flex-row justify-center items-center space-x-4">
|
||||
<.link :if={Accounts.allow_registration?()} href={~p"/users/register"} class="btn btn-primary">
|
||||
<%= dgettext("actions", "register") %>
|
||||
</.link>
|
||||
<.link href={~p"/users/log_in"} class="btn btn-primary">
|
||||
<%= dgettext("actions", "log in") %>
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
@ -5,7 +5,7 @@ defmodule MemexWeb.UserSessionController do
|
||||
alias MemexWeb.UserAuth
|
||||
|
||||
def new(conn, _params) do
|
||||
render(conn, "new.html", error_message: nil, page_title: gettext("log in"))
|
||||
render(conn, :new, error_message: nil, page_title: gettext("log in"))
|
||||
end
|
||||
|
||||
def create(conn, %{"user" => user_params}) do
|
||||
@ -14,7 +14,7 @@ defmodule MemexWeb.UserSessionController do
|
||||
if user = Accounts.get_user_by_email_and_password(email, password) do
|
||||
UserAuth.log_in_user(conn, user, user_params)
|
||||
else
|
||||
render(conn, "new.html", error_message: dgettext("errors", "Invalid email or password"))
|
||||
render(conn, :new, error_message: dgettext("errors", "Invalid email or password"))
|
||||
end
|
||||
end
|
||||
|
||||
|
6
lib/memex_web/controllers/user_session_html.ex
Normal file
6
lib/memex_web/controllers/user_session_html.ex
Normal file
@ -0,0 +1,6 @@
|
||||
defmodule MemexWeb.UserSessionHTML do
|
||||
use MemexWeb, :html
|
||||
alias Memex.Accounts
|
||||
|
||||
embed_templates "user_session_html/*"
|
||||
end
|
41
lib/memex_web/controllers/user_session_html/new.html.heex
Normal file
41
lib/memex_web/controllers/user_session_html/new.html.heex
Normal file
@ -0,0 +1,41 @@
|
||||
<div class="mx-auto pb-8 max-w-2xl flex flex-col justify-center items-center space-y-4">
|
||||
<h1 class="title text-primary-400 text-xl">
|
||||
<%= dgettext("actions", "log in") %>
|
||||
</h1>
|
||||
|
||||
<.form
|
||||
:let={f}
|
||||
for={@conn}
|
||||
action={~p"/users/log_in"}
|
||||
as={:user}
|
||||
class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
|
||||
>
|
||||
<p :if={@error_message} class="alert alert-danger col-span-3">
|
||||
<%= @error_message %>
|
||||
</p>
|
||||
|
||||
<%= label(f, :email, gettext("email"), class: "title text-lg text-primary-400") %>
|
||||
<%= email_input(f, :email, required: true, class: "input input-primary col-span-2") %>
|
||||
|
||||
<%= label(f, :password, gettext("password"), class: "title text-lg text-primary-400") %>
|
||||
<%= password_input(f, :password, required: true, class: "input input-primary col-span-2") %>
|
||||
|
||||
<%= label(f, :remember_me, gettext("keep me logged in for 60 days"),
|
||||
class: "title text-lg text-primary-400"
|
||||
) %>
|
||||
<%= checkbox(f, :remember_me, class: "checkbox col-span-2") %>
|
||||
|
||||
<%= submit(dgettext("actions", "log in"), class: "mx-auto btn btn-primary col-span-3") %>
|
||||
</.form>
|
||||
|
||||
<hr class="hr" />
|
||||
|
||||
<div class="flex flex-row justify-center items-center space-x-4">
|
||||
<.link :if={Accounts.allow_registration?()} href={~p"/users/register"} class="btn btn-primary">
|
||||
<%= dgettext("actions", "register") %>
|
||||
</.link>
|
||||
<.link href={~p"/users/reset_password"} class="btn btn-primary">
|
||||
<%= dgettext("actions", "forgot your password?") %>
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
@ -2,12 +2,12 @@ defmodule MemexWeb.UserSettingsController do
|
||||
use MemexWeb, :controller
|
||||
import MemexWeb.Gettext
|
||||
alias Memex.Accounts
|
||||
alias MemexWeb.{HomeLive, UserAuth}
|
||||
alias MemexWeb.UserAuth
|
||||
|
||||
plug :assign_email_and_password_changesets
|
||||
|
||||
def edit(conn, _params) do
|
||||
render(conn, "edit.html", page_title: gettext("settings"))
|
||||
render(conn, :edit, page_title: gettext("settings"))
|
||||
end
|
||||
|
||||
def update(%{assigns: %{current_user: user}} = conn, %{
|
||||
@ -20,7 +20,7 @@ defmodule MemexWeb.UserSettingsController do
|
||||
Accounts.deliver_update_email_instructions(
|
||||
applied_user,
|
||||
user.email,
|
||||
&Routes.user_settings_url(conn, :confirm_email, &1)
|
||||
fn token -> url(MemexWeb.Endpoint, ~p"/users/settings/confirm_email/#{token}") end
|
||||
)
|
||||
|
||||
conn
|
||||
@ -31,10 +31,10 @@ defmodule MemexWeb.UserSettingsController do
|
||||
"a link to confirm your email change has been sent to the new address."
|
||||
)
|
||||
)
|
||||
|> redirect(to: Routes.user_settings_path(conn, :edit))
|
||||
|> redirect(to: ~p"/users/settings")
|
||||
|
||||
{:error, changeset} ->
|
||||
conn |> render("edit.html", email_changeset: changeset)
|
||||
conn |> render(:edit, email_changeset: changeset)
|
||||
end
|
||||
end
|
||||
|
||||
@ -47,11 +47,11 @@ defmodule MemexWeb.UserSettingsController do
|
||||
{:ok, user} ->
|
||||
conn
|
||||
|> put_flash(:info, dgettext("prompts", "password updated successfully."))
|
||||
|> put_session(:user_return_to, Routes.user_settings_path(conn, :edit))
|
||||
|> put_session(:user_return_to, ~p"/users/settings")
|
||||
|> UserAuth.log_in_user(user)
|
||||
|
||||
{:error, changeset} ->
|
||||
conn |> render("edit.html", password_changeset: changeset)
|
||||
conn |> render(:edit, password_changeset: changeset)
|
||||
end
|
||||
end
|
||||
|
||||
@ -63,10 +63,10 @@ defmodule MemexWeb.UserSettingsController do
|
||||
{:ok, _user} ->
|
||||
conn
|
||||
|> put_flash(:info, dgettext("prompts", "language updated successfully."))
|
||||
|> redirect(to: Routes.user_settings_path(conn, :edit))
|
||||
|> redirect(to: ~p"/users/settings")
|
||||
|
||||
{:error, changeset} ->
|
||||
conn |> render("edit.html", locale_changeset: changeset)
|
||||
conn |> render(:edit, locale_changeset: changeset)
|
||||
end
|
||||
end
|
||||
|
||||
@ -75,7 +75,7 @@ defmodule MemexWeb.UserSettingsController do
|
||||
:ok ->
|
||||
conn
|
||||
|> put_flash(:info, dgettext("prompts", "email changed successfully."))
|
||||
|> redirect(to: Routes.user_settings_path(conn, :edit))
|
||||
|> redirect(to: ~p"/users/settings")
|
||||
|
||||
:error ->
|
||||
conn
|
||||
@ -83,7 +83,7 @@ defmodule MemexWeb.UserSettingsController do
|
||||
:error,
|
||||
dgettext("errors", "email change link is invalid or it has expired.")
|
||||
)
|
||||
|> redirect(to: Routes.user_settings_path(conn, :edit))
|
||||
|> redirect(to: ~p"/users/settings")
|
||||
end
|
||||
end
|
||||
|
||||
@ -93,11 +93,11 @@ defmodule MemexWeb.UserSettingsController do
|
||||
|
||||
conn
|
||||
|> put_flash(:error, dgettext("prompts", "your account has been deleted"))
|
||||
|> redirect(to: Routes.live_path(conn, HomeLive))
|
||||
|> redirect(to: ~p"/")
|
||||
else
|
||||
conn
|
||||
|> put_flash(:error, dgettext("errors", "unable to delete user"))
|
||||
|> redirect(to: Routes.user_settings_path(conn, :edit))
|
||||
|> redirect(to: ~p"/users/settings")
|
||||
end
|
||||
end
|
||||
|
||||
|
5
lib/memex_web/controllers/user_settings_html.ex
Normal file
5
lib/memex_web/controllers/user_settings_html.ex
Normal file
@ -0,0 +1,5 @@
|
||||
defmodule MemexWeb.UserSettingsHTML do
|
||||
use MemexWeb, :html
|
||||
|
||||
embed_templates "user_settings_html/*"
|
||||
end
|
150
lib/memex_web/controllers/user_settings_html/edit.html.heex
Normal file
150
lib/memex_web/controllers/user_settings_html/edit.html.heex
Normal file
@ -0,0 +1,150 @@
|
||||
<div class="mx-auto pb-8 max-w-3xl flex flex-col justify-center items-stretch text-right space-y-4">
|
||||
<h1 class="title text-primary-400 text-xl text-left">
|
||||
<%= gettext("settings") %>
|
||||
</h1>
|
||||
|
||||
<hr class="hr" />
|
||||
|
||||
<.form
|
||||
:let={f}
|
||||
for={@email_changeset}
|
||||
action={~p"/users/settings"}
|
||||
class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
|
||||
>
|
||||
<h3 class="title text-primary-400 text-lg text-center col-span-3">
|
||||
<%= dgettext("actions", "change email") %>
|
||||
</h3>
|
||||
|
||||
<div
|
||||
:if={@email_changeset.action && not @email_changeset.valid?()}
|
||||
class="alert alert-danger col-span-3"
|
||||
>
|
||||
<%= dgettext("errors", "oops, something went wrong! please check the errors below") %>
|
||||
</div>
|
||||
|
||||
<%= hidden_input(f, :action, name: "action", value: "update_email") %>
|
||||
|
||||
<%= label(f, :email, gettext("email"), class: "title text-lg text-primary-400") %>
|
||||
<%= email_input(f, :email, required: true, class: "mx-2 my-1 input input-primary col-span-2") %>
|
||||
<%= error_tag(f, :email, "col-span-3") %>
|
||||
|
||||
<%= label(f, :current_password, gettext("current password"),
|
||||
for: "current_password_for_email",
|
||||
class: "mx-2 my-1 title text-lg text-primary-400"
|
||||
) %>
|
||||
<%= password_input(f, :current_password,
|
||||
required: true,
|
||||
name: "current_password",
|
||||
id: "current_password_for_email",
|
||||
class: "mx-2 my-1 input input-primary col-span-2"
|
||||
) %>
|
||||
<%= error_tag(f, :current_password, "col-span-3") %>
|
||||
|
||||
<%= submit(dgettext("actions", "change email"),
|
||||
class: "mx-auto btn btn-primary col-span-3"
|
||||
) %>
|
||||
</.form>
|
||||
|
||||
<hr class="hr" />
|
||||
|
||||
<.form
|
||||
:let={f}
|
||||
for={@password_changeset}
|
||||
action={~p"/users/settings"}
|
||||
class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
|
||||
>
|
||||
<h3 class="title text-primary-400 text-lg text-center col-span-3">
|
||||
<%= dgettext("actions", "change password") %>
|
||||
</h3>
|
||||
|
||||
<p
|
||||
:if={@password_changeset.action && not @password_changeset.valid?()}
|
||||
class="alert alert-danger col-span-3"
|
||||
>
|
||||
<%= dgettext("errors", "oops, something went wrong! please check the errors below.") %>
|
||||
</p>
|
||||
|
||||
<%= hidden_input(f, :action, name: "action", value: "update_password") %>
|
||||
|
||||
<%= label(f, :password, gettext("new password"), class: "title text-lg text-primary-400") %>
|
||||
<%= password_input(f, :password,
|
||||
required: true,
|
||||
class: "mx-2 my-1 input input-primary col-span-2"
|
||||
) %>
|
||||
<%= error_tag(f, :password, "col-span-3") %>
|
||||
|
||||
<%= label(f, :password_confirmation, gettext("confirm new password"),
|
||||
class: "title text-lg text-primary-400"
|
||||
) %>
|
||||
<%= password_input(f, :password_confirmation,
|
||||
required: true,
|
||||
class: "mx-2 my-1 input input-primary col-span-2"
|
||||
) %>
|
||||
<%= error_tag(f, :password_confirmation, "col-span-3") %>
|
||||
|
||||
<%= label(f, :current_password, gettext("current password"),
|
||||
for: "current_password_for_password",
|
||||
class: "title text-lg text-primary-400"
|
||||
) %>
|
||||
<%= password_input(f, :current_password,
|
||||
required: true,
|
||||
name: "current_password",
|
||||
id: "current_password_for_password",
|
||||
class: "mx-2 my-1 input input-primary col-span-2"
|
||||
) %>
|
||||
<%= error_tag(f, :current_password, "col-span-3") %>
|
||||
|
||||
<%= submit(dgettext("actions", "change password"),
|
||||
class: "mx-auto btn btn-primary col-span-3"
|
||||
) %>
|
||||
</.form>
|
||||
|
||||
<hr class="hr" />
|
||||
|
||||
<.form
|
||||
:let={f}
|
||||
for={@locale_changeset}
|
||||
action={~p"/users/settings"}
|
||||
class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
|
||||
>
|
||||
<%= label(f, :locale, dgettext("actions", "change language"),
|
||||
class: "title text-primary-400 text-lg text-center col-span-3"
|
||||
) %>
|
||||
|
||||
<div
|
||||
:if={@locale_changeset.action && not @locale_changeset.valid?()}
|
||||
class="alert alert-danger col-span-3"
|
||||
>
|
||||
<%= dgettext("errors", "oops, something went wrong! please check the errors below") %>
|
||||
</div>
|
||||
|
||||
<%= hidden_input(f, :action, name: "action", value: "update_locale") %>
|
||||
|
||||
<%= select(f, :locale, [{gettext("english"), "en_US"}, {"spanish", "es"}],
|
||||
class: "mx-2 my-1 min-w-md input input-primary col-start-2"
|
||||
) %>
|
||||
<%= error_tag(f, :locale, "col-span-3") %>
|
||||
|
||||
<%= submit(dgettext("actions", "change language"),
|
||||
class: "whitespace-nowrap mx-auto btn btn-primary col-span-3",
|
||||
data: [qa: dgettext("prompts", "are you sure you want to change your language?")]
|
||||
) %>
|
||||
</.form>
|
||||
|
||||
<hr class="hr" />
|
||||
|
||||
<div class="flex justify-end items-center">
|
||||
<.link href={~p"/export/json"} class="mx-4 my-2 btn btn-primary" target="_blank">
|
||||
<%= dgettext("actions", "export data as json") %>
|
||||
</.link>
|
||||
|
||||
<.link
|
||||
href={~p"/users/settings/#{@current_user}"}
|
||||
method="delete"
|
||||
class="mx-4 my-2 btn btn-alert"
|
||||
data-confirm={dgettext("prompts", "are you sure you want to delete your account?")}
|
||||
>
|
||||
<%= dgettext("actions", "delete user") %>
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
Reference in New Issue
Block a user