update controllers and auth
This commit is contained in:
		
							
								
								
									
										23
									
								
								lib/lokal_web/controllers/email_controller.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								lib/lokal_web/controllers/email_controller.ex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | defmodule LokalWeb.EmailController do | ||||||
|  |   @moduledoc """ | ||||||
|  |   A dev controller used to develop on emails | ||||||
|  |   """ | ||||||
|  |  | ||||||
|  |   use LokalWeb, :controller | ||||||
|  |   alias Lokal.Accounts.User | ||||||
|  |  | ||||||
|  |   plug :put_layout, {LokalWeb.LayoutView, :email} | ||||||
|  |  | ||||||
|  |   @sample_assigns %{ | ||||||
|  |     email: %{subject: "Example subject"}, | ||||||
|  |     url: "https://lokal.bubbletea.dev/sample_url", | ||||||
|  |     user: %User{email: "sample@email.com"} | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @doc """ | ||||||
|  |   Debug route used to preview emails | ||||||
|  |   """ | ||||||
|  |   def preview(conn, %{"id" => template}) do | ||||||
|  |     render(conn, "#{template |> to_string()}.html", @sample_assigns) | ||||||
|  |   end | ||||||
|  | end | ||||||
| @@ -1,12 +1,13 @@ | |||||||
| defmodule LokalWeb.UserAuth do | defmodule LokalWeb.UserAuth do | ||||||
|   @moduledoc """ |   @moduledoc """ | ||||||
|   Module for any user authentication functions |   Functions for user session and authentication | ||||||
|   """ |   """ | ||||||
|  |  | ||||||
|   import Plug.Conn |   import Plug.Conn | ||||||
|   import Phoenix.Controller |   import Phoenix.Controller | ||||||
|   import LokalWeb.Gettext |   import LokalWeb.Gettext | ||||||
|   alias Lokal.{Accounts, Accounts.User} |   alias Lokal.{Accounts, Accounts.User} | ||||||
|  |   alias LokalWeb.PageLive | ||||||
|   alias LokalWeb.Router.Helpers, as: Routes |   alias LokalWeb.Router.Helpers, as: Routes | ||||||
|  |  | ||||||
|   # Make the remember me cookie valid for 60 days. |   # Make the remember me cookie valid for 60 days. | ||||||
| @@ -32,6 +33,7 @@ defmodule LokalWeb.UserAuth do | |||||||
|  |  | ||||||
|   def log_in_user(conn, %User{confirmed_at: nil}, _params) do |   def log_in_user(conn, %User{confirmed_at: nil}, _params) do | ||||||
|     conn |     conn | ||||||
|  |     |> fetch_flash() | ||||||
|     |> put_flash( |     |> put_flash( | ||||||
|       :error, |       :error, | ||||||
|       dgettext("errors", "You must confirm your account and log in to access this page.") |       dgettext("errors", "You must confirm your account and log in to access this page.") | ||||||
| @@ -53,6 +55,11 @@ defmodule LokalWeb.UserAuth do | |||||||
|     |> redirect(to: user_return_to || signed_in_path(conn)) |     |> redirect(to: user_return_to || signed_in_path(conn)) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   @spec maybe_write_remember_me_cookie( | ||||||
|  |           Plug.Conn.t(), | ||||||
|  |           String.t() | any(), | ||||||
|  |           %{required(String.t()) => String.t()} | any() | ||||||
|  |         ) :: Plug.Conn.t() | ||||||
|   defp maybe_write_remember_me_cookie(conn, token, %{"remember_me" => "true"}) do |   defp maybe_write_remember_me_cookie(conn, token, %{"remember_me" => "true"}) do | ||||||
|     put_resp_cookie(conn, @remember_me_cookie, token, @remember_me_options) |     put_resp_cookie(conn, @remember_me_cookie, token, @remember_me_options) | ||||||
|   end |   end | ||||||
| @@ -149,13 +156,31 @@ defmodule LokalWeb.UserAuth do | |||||||
|       conn |       conn | ||||||
|     else |     else | ||||||
|       conn |       conn | ||||||
|       |> put_flash(:error, "You must confirm your account and log in to access this page.") |       |> put_flash( | ||||||
|  |         :error, | ||||||
|  |         dgettext("errors", "You must confirm your account and log in to access this page.") | ||||||
|  |       ) | ||||||
|       |> maybe_store_return_to() |       |> maybe_store_return_to() | ||||||
|       |> redirect(to: Routes.user_session_path(conn, :new)) |       |> redirect(to: Routes.user_session_path(conn, :new)) | ||||||
|       |> halt() |       |> halt() | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   @doc """ | ||||||
|  |   Used for routes that require the user to be an admin. | ||||||
|  |   """ | ||||||
|  |   def require_role(conn, role: role_atom) do | ||||||
|  |     if conn.assigns[:current_user] && conn.assigns.current_user.role == role_atom do | ||||||
|  |       conn | ||||||
|  |     else | ||||||
|  |       conn | ||||||
|  |       |> put_flash(:error, dgettext("errors", "You are not authorized to view this page.")) | ||||||
|  |       |> maybe_store_return_to() | ||||||
|  |       |> redirect(to: Routes.live_path(conn, PageLive)) | ||||||
|  |       |> halt() | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|   defp maybe_store_return_to(%{method: "GET"} = conn) do |   defp maybe_store_return_to(%{method: "GET"} = conn) do | ||||||
|     put_session(conn, :user_return_to, current_path(conn)) |     put_session(conn, :user_return_to, current_path(conn)) | ||||||
|   end |   end | ||||||
|   | |||||||
| @@ -1,10 +1,11 @@ | |||||||
| defmodule LokalWeb.UserConfirmationController do | defmodule LokalWeb.UserConfirmationController do | ||||||
|   use LokalWeb, :controller |   use LokalWeb, :controller | ||||||
|  |  | ||||||
|  |   import LokalWeb.Gettext | ||||||
|   alias Lokal.Accounts |   alias Lokal.Accounts | ||||||
|  |  | ||||||
|   def new(conn, _params) do |   def new(conn, _params) do | ||||||
|     render(conn, "new.html") |     render(conn, "new.html", page_title: gettext("Confirm your account")) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def create(conn, %{"user" => %{"email" => email}}) do |   def create(conn, %{"user" => %{"email" => email}}) do | ||||||
| @@ -19,8 +20,11 @@ defmodule LokalWeb.UserConfirmationController do | |||||||
|     conn |     conn | ||||||
|     |> put_flash( |     |> put_flash( | ||||||
|       :info, |       :info, | ||||||
|       "If your email is in our system and it has not been confirmed yet, " <> |       dgettext( | ||||||
|         "you will receive an email with instructions shortly." |         "prompts", | ||||||
|  |         "If your email is in our system and it has not been confirmed yet, " <> | ||||||
|  |           "you will receive an email with instructions shortly." | ||||||
|  |       ) | ||||||
|     ) |     ) | ||||||
|     |> redirect(to: "/") |     |> redirect(to: "/") | ||||||
|   end |   end | ||||||
| @@ -29,9 +33,9 @@ defmodule LokalWeb.UserConfirmationController do | |||||||
|   # leaked token giving the user access to the account. |   # leaked token giving the user access to the account. | ||||||
|   def confirm(conn, %{"token" => token}) do |   def confirm(conn, %{"token" => token}) do | ||||||
|     case Accounts.confirm_user(token) do |     case Accounts.confirm_user(token) do | ||||||
|       {:ok, _} -> |       {:ok, %{email: email}} -> | ||||||
|         conn |         conn | ||||||
|         |> put_flash(:info, "User confirmed successfully.") |         |> put_flash(:info, dgettext("prompts", "%{email} confirmed successfully.", email: email)) | ||||||
|         |> redirect(to: "/") |         |> redirect(to: "/") | ||||||
|  |  | ||||||
|       :error -> |       :error -> | ||||||
| @@ -45,7 +49,10 @@ defmodule LokalWeb.UserConfirmationController do | |||||||
|  |  | ||||||
|           %{} -> |           %{} -> | ||||||
|             conn |             conn | ||||||
|             |> put_flash(:error, "User confirmation link is invalid or it has expired.") |             |> put_flash( | ||||||
|  |               :error, | ||||||
|  |               dgettext("errors", "User confirmation link is invalid or it has expired.") | ||||||
|  |             ) | ||||||
|             |> redirect(to: "/") |             |> redirect(to: "/") | ||||||
|         end |         end | ||||||
|     end |     end | ||||||
|   | |||||||
| @@ -1,30 +1,81 @@ | |||||||
| defmodule LokalWeb.UserRegistrationController do | defmodule LokalWeb.UserRegistrationController do | ||||||
|   use LokalWeb, :controller |   use LokalWeb, :controller | ||||||
|  |   import LokalWeb.Gettext | ||||||
|   alias Lokal.Accounts |   alias Lokal.{Accounts, Invites} | ||||||
|   alias Lokal.Accounts.User |   alias Lokal.Accounts.User | ||||||
|   alias LokalWeb.UserAuth |   alias LokalWeb.{Endpoint, PageLive} | ||||||
|  |  | ||||||
|   def new(conn, _params) do |   def new(conn, %{"invite" => invite_token}) do | ||||||
|     changeset = Accounts.change_user_registration(%User{}) |     invite = Invites.get_invite_by_token(invite_token) | ||||||
|     render(conn, "new.html", changeset: changeset) |  | ||||||
|  |     if invite do | ||||||
|  |       conn |> render_new(invite) | ||||||
|  |     else | ||||||
|  |       conn | ||||||
|  |       |> put_flash(:error, dgettext("errors", "Sorry, this invite was not found or expired")) | ||||||
|  |       |> redirect(to: Routes.live_path(Endpoint, PageLive)) | ||||||
|  |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def create(conn, %{"user" => user_params}) do |   def new(conn, _params) do | ||||||
|  |     if Accounts.allow_registration?() do | ||||||
|  |       conn |> render_new() | ||||||
|  |     else | ||||||
|  |       conn | ||||||
|  |       |> put_flash(:error, dgettext("errors", "Sorry, public registration is disabled")) | ||||||
|  |       |> redirect(to: Routes.live_path(Endpoint, PageLive)) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   # renders new user registration page | ||||||
|  |   defp render_new(conn, invite \\ nil) do | ||||||
|  |     render(conn, "new.html", | ||||||
|  |       changeset: Accounts.change_user_registration(%User{}), | ||||||
|  |       invite: invite, | ||||||
|  |       page_title: gettext("Register") | ||||||
|  |     ) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def create(conn, %{"user" => %{"invite_token" => invite_token}} = attrs) do | ||||||
|  |     invite = Invites.get_invite_by_token(invite_token) | ||||||
|  |  | ||||||
|  |     if invite do | ||||||
|  |       conn |> create_user(attrs, invite) | ||||||
|  |     else | ||||||
|  |       conn | ||||||
|  |       |> put_flash(:error, dgettext("errors", "Sorry, this invite was not found or expired")) | ||||||
|  |       |> redirect(to: Routes.live_path(Endpoint, PageLive)) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def create(conn, attrs) do | ||||||
|  |     if Accounts.allow_registration?() do | ||||||
|  |       conn |> create_user(attrs) | ||||||
|  |     else | ||||||
|  |       conn | ||||||
|  |       |> put_flash(:error, dgettext("errors", "Sorry, public registration is disabled")) | ||||||
|  |       |> redirect(to: Routes.live_path(Endpoint, PageLive)) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   defp create_user(conn, %{"user" => user_params}, invite \\ nil) do | ||||||
|     case Accounts.register_user(user_params) do |     case Accounts.register_user(user_params) do | ||||||
|       {:ok, user} -> |       {:ok, user} -> | ||||||
|         {:ok, _} = |         unless invite |> is_nil() do | ||||||
|           Accounts.deliver_user_confirmation_instructions( |           invite |> Invites.use_invite!() | ||||||
|             user, |         end | ||||||
|             &Routes.user_confirmation_url(conn, :confirm, &1) |  | ||||||
|           ) |         Accounts.deliver_user_confirmation_instructions( | ||||||
|  |           user, | ||||||
|  |           &Routes.user_confirmation_url(conn, :confirm, &1) | ||||||
|  |         ) | ||||||
|  |  | ||||||
|         conn |         conn | ||||||
|         |> put_flash(:info, "User created successfully.") |         |> put_flash(:info, dgettext("prompts", "Please check your email to verify your account")) | ||||||
|         |> UserAuth.log_in_user(user) |         |> redirect(to: Routes.user_session_path(Endpoint, :new)) | ||||||
|  |  | ||||||
|       {:error, %Ecto.Changeset{} = changeset} -> |       {:error, %Ecto.Changeset{} = changeset} -> | ||||||
|         render(conn, "new.html", changeset: changeset) |         conn |> render("new.html", changeset: changeset, invite: invite) | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ defmodule LokalWeb.UserResetPasswordController do | |||||||
|   plug :get_user_by_reset_password_token when action in [:edit, :update] |   plug :get_user_by_reset_password_token when action in [:edit, :update] | ||||||
|  |  | ||||||
|   def new(conn, _params) do |   def new(conn, _params) do | ||||||
|     render(conn, "new.html") |     render(conn, "new.html", page_title: gettext("Forgot your password?")) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def create(conn, %{"user" => %{"email" => email}}) do |   def create(conn, %{"user" => %{"email" => email}}) do | ||||||
| @@ -21,13 +21,20 @@ defmodule LokalWeb.UserResetPasswordController do | |||||||
|     conn |     conn | ||||||
|     |> put_flash( |     |> put_flash( | ||||||
|       :info, |       :info, | ||||||
|       "If your email is in our system, you will receive instructions to reset your password shortly." |       dgettext( | ||||||
|  |         "prompts", | ||||||
|  |         "If your email is in our system, you will receive instructions to " <> | ||||||
|  |           "reset your password shortly." | ||||||
|  |       ) | ||||||
|     ) |     ) | ||||||
|     |> redirect(to: "/") |     |> redirect(to: "/") | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def edit(conn, _params) do |   def edit(conn, _params) do | ||||||
|     render(conn, "edit.html", changeset: Accounts.change_user_password(conn.assigns.user)) |     render(conn, "edit.html", | ||||||
|  |       changeset: Accounts.change_user_password(conn.assigns.user), | ||||||
|  |       page_title: gettext("Reset your password") | ||||||
|  |     ) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   # Do not log in the user after reset password to avoid a |   # Do not log in the user after reset password to avoid a | ||||||
| @@ -36,7 +43,7 @@ defmodule LokalWeb.UserResetPasswordController do | |||||||
|     case Accounts.reset_user_password(conn.assigns.user, user_params) do |     case Accounts.reset_user_password(conn.assigns.user, user_params) do | ||||||
|       {:ok, _} -> |       {:ok, _} -> | ||||||
|         conn |         conn | ||||||
|         |> put_flash(:info, "Password reset successfully.") |         |> put_flash(:info, dgettext("prompts", "Password reset successfully.")) | ||||||
|         |> redirect(to: Routes.user_session_path(conn, :new)) |         |> redirect(to: Routes.user_session_path(conn, :new)) | ||||||
|  |  | ||||||
|       {:error, changeset} -> |       {:error, changeset} -> | ||||||
| @@ -51,7 +58,10 @@ defmodule LokalWeb.UserResetPasswordController do | |||||||
|       conn |> assign(:user, user) |> assign(:token, token) |       conn |> assign(:user, user) |> assign(:token, token) | ||||||
|     else |     else | ||||||
|       conn |       conn | ||||||
|       |> put_flash(:error, "Reset password link is invalid or it has expired.") |       |> put_flash( | ||||||
|  |         :error, | ||||||
|  |         dgettext("errors", "Reset password link is invalid or it has expired.") | ||||||
|  |       ) | ||||||
|       |> redirect(to: "/") |       |> redirect(to: "/") | ||||||
|       |> halt() |       |> halt() | ||||||
|     end |     end | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ defmodule LokalWeb.UserSessionController do | |||||||
|   alias LokalWeb.UserAuth |   alias LokalWeb.UserAuth | ||||||
|  |  | ||||||
|   def new(conn, _params) do |   def new(conn, _params) do | ||||||
|     render(conn, "new.html", error_message: nil) |     render(conn, "new.html", error_message: nil, page_title: gettext("Log in")) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def create(conn, %{"user" => user_params}) do |   def create(conn, %{"user" => user_params}) do | ||||||
| @@ -14,13 +14,13 @@ defmodule LokalWeb.UserSessionController do | |||||||
|     if user = Accounts.get_user_by_email_and_password(email, password) do |     if user = Accounts.get_user_by_email_and_password(email, password) do | ||||||
|       UserAuth.log_in_user(conn, user, user_params) |       UserAuth.log_in_user(conn, user, user_params) | ||||||
|     else |     else | ||||||
|       render(conn, "new.html", error_message: "Invalid email or password") |       render(conn, "new.html", error_message: dgettext("errors", "Invalid email or password")) | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def delete(conn, _params) do |   def delete(conn, _params) do | ||||||
|     conn |     conn | ||||||
|     |> put_flash(:info, "Logged out successfully.") |     |> put_flash(:info, dgettext("prompts", "Logged out successfully.")) | ||||||
|     |> UserAuth.log_out_user() |     |> UserAuth.log_out_user() | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -1,13 +1,13 @@ | |||||||
| defmodule LokalWeb.UserSettingsController do | defmodule LokalWeb.UserSettingsController do | ||||||
|   use LokalWeb, :controller |   use LokalWeb, :controller | ||||||
|  |   import LokalWeb.Gettext | ||||||
|   alias Lokal.Accounts |   alias Lokal.Accounts | ||||||
|   alias LokalWeb.UserAuth |   alias LokalWeb.{PageLive, UserAuth} | ||||||
|  |  | ||||||
|   plug :assign_email_and_password_changesets |   plug :assign_email_and_password_changesets | ||||||
|  |  | ||||||
|   def edit(conn, _params) do |   def edit(conn, _params) do | ||||||
|     render(conn, "edit.html") |     render(conn, "edit.html", page_title: gettext("Settings")) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def update(conn, %{"action" => "update_email"} = params) do |   def update(conn, %{"action" => "update_email"} = params) do | ||||||
| @@ -25,7 +25,10 @@ defmodule LokalWeb.UserSettingsController do | |||||||
|         conn |         conn | ||||||
|         |> put_flash( |         |> put_flash( | ||||||
|           :info, |           :info, | ||||||
|           "A link to confirm your email change has been sent to the new address." |           dgettext( | ||||||
|  |             "prompts", | ||||||
|  |             "A link to confirm your email change has been sent to the new address." | ||||||
|  |           ) | ||||||
|         ) |         ) | ||||||
|         |> redirect(to: Routes.user_settings_path(conn, :edit)) |         |> redirect(to: Routes.user_settings_path(conn, :edit)) | ||||||
|  |  | ||||||
| @@ -41,7 +44,7 @@ defmodule LokalWeb.UserSettingsController do | |||||||
|     case Accounts.update_user_password(user, password, user_params) do |     case Accounts.update_user_password(user, password, user_params) do | ||||||
|       {:ok, user} -> |       {:ok, user} -> | ||||||
|         conn |         conn | ||||||
|         |> put_flash(:info, "Password updated successfully.") |         |> put_flash(:info, dgettext("prompts", "Password updated successfully.")) | ||||||
|         |> put_session(:user_return_to, Routes.user_settings_path(conn, :edit)) |         |> put_session(:user_return_to, Routes.user_settings_path(conn, :edit)) | ||||||
|         |> UserAuth.log_in_user(user) |         |> UserAuth.log_in_user(user) | ||||||
|  |  | ||||||
| @@ -54,16 +57,33 @@ defmodule LokalWeb.UserSettingsController do | |||||||
|     case Accounts.update_user_email(conn.assigns.current_user, token) do |     case Accounts.update_user_email(conn.assigns.current_user, token) do | ||||||
|       :ok -> |       :ok -> | ||||||
|         conn |         conn | ||||||
|         |> put_flash(:info, "Email changed successfully.") |         |> put_flash(:info, dgettext("prompts", "Email changed successfully.")) | ||||||
|         |> redirect(to: Routes.user_settings_path(conn, :edit)) |         |> redirect(to: Routes.user_settings_path(conn, :edit)) | ||||||
|  |  | ||||||
|       :error -> |       :error -> | ||||||
|         conn |         conn | ||||||
|         |> put_flash(:error, "Email change link is invalid or it has expired.") |         |> put_flash( | ||||||
|  |           :error, | ||||||
|  |           dgettext("errors", "Email change link is invalid or it has expired.") | ||||||
|  |         ) | ||||||
|         |> redirect(to: Routes.user_settings_path(conn, :edit)) |         |> redirect(to: Routes.user_settings_path(conn, :edit)) | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   def delete(%{assigns: %{current_user: current_user}} = conn, %{"id" => user_id}) do | ||||||
|  |     if user_id == current_user.id do | ||||||
|  |       current_user |> Accounts.delete_user!(current_user) | ||||||
|  |  | ||||||
|  |       conn | ||||||
|  |       |> put_flash(:error, dgettext("prompts", "Your account has been deleted")) | ||||||
|  |       |> redirect(to: Routes.live_path(conn, PageLive)) | ||||||
|  |     else | ||||||
|  |       conn | ||||||
|  |       |> put_flash(:error, dgettext("errors", "Unable to delete user")) | ||||||
|  |       |> redirect(to: Routes.user_settings_path(conn, :edit)) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|   defp assign_email_and_password_changesets(conn, _opts) do |   defp assign_email_and_password_changesets(conn, _opts) do | ||||||
|     user = conn.assigns.current_user |     user = conn.assigns.current_user | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,22 +4,31 @@ defmodule LokalWeb.ErrorHelpers do | |||||||
|   """ |   """ | ||||||
|  |  | ||||||
|   use Phoenix.HTML |   use Phoenix.HTML | ||||||
|  |   import Phoenix.LiveView.Helpers | ||||||
|  |   alias Ecto.Changeset | ||||||
|  |   alias Phoenix.{HTML.Form, LiveView.Rendered} | ||||||
|  |  | ||||||
|   @doc """ |   @doc """ | ||||||
|   Generates tag for inlined form input errors. |   Generates tag for inlined form input errors. | ||||||
|   """ |   """ | ||||||
|   def error_tag(form, field) do |   @spec error_tag(Form.t(), Form.field()) :: Rendered.t() | ||||||
|     Enum.map(Keyword.get_values(form.errors, field), fn error -> |   @spec error_tag(Form.t(), Form.field(), String.t()) :: Rendered.t() | ||||||
|       content_tag(:span, translate_error(error), |   def error_tag(form, field, extra_class \\ "") do | ||||||
|         class: "invalid-feedback", |     assigns = %{extra_class: extra_class, form: form, field: field} | ||||||
|         phx_feedback_for: input_name(form, field) |  | ||||||
|       ) |     ~H""" | ||||||
|     end) |     <%= for error <- Keyword.get_values(@form.errors, @field) do %> | ||||||
|  |       <span class={"invalid-feedback #{@extra_class}"} phx-feedback-for={input_name(@form, @field)}> | ||||||
|  |         <%= translate_error(error) %> | ||||||
|  |       </span> | ||||||
|  |     <% end %> | ||||||
|  |     """ | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   @doc """ |   @doc """ | ||||||
|   Translates an error message using gettext. |   Translates an error message using gettext. | ||||||
|   """ |   """ | ||||||
|  |   @spec translate_error({String.t(), keyword() | map()}) :: String.t() | ||||||
|   def translate_error({msg, opts}) do |   def translate_error({msg, opts}) do | ||||||
|     # When using gettext, we typically pass the strings we want |     # When using gettext, we typically pass the strings we want | ||||||
|     # to translate as a static argument: |     # to translate as a static argument: | ||||||
| @@ -44,4 +53,30 @@ defmodule LokalWeb.ErrorHelpers do | |||||||
|       Gettext.dgettext(LokalWeb.Gettext, "errors", msg, opts) |       Gettext.dgettext(LokalWeb.Gettext, "errors", msg, opts) | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   @doc """ | ||||||
|  |   Displays all errors from a changeset, or just for a single key | ||||||
|  |   """ | ||||||
|  |   @spec changeset_errors(Changeset.t()) :: String.t() | ||||||
|  |   @spec changeset_errors(Changeset.t(), key :: atom()) :: [String.t()] | nil | ||||||
|  |   def changeset_errors(changeset) do | ||||||
|  |     changeset | ||||||
|  |     |> changeset_error_map() | ||||||
|  |     |> Enum.map_join(". ", fn {key, errors} -> | ||||||
|  |       "#{key |> humanize()}: #{errors |> Enum.join(", ")}" | ||||||
|  |     end) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def changeset_errors(changeset, key) do | ||||||
|  |     changeset |> changeset_error_map() |> Map.get(key) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   @doc """ | ||||||
|  |   Displays all errors from a changeset in a key value map | ||||||
|  |   """ | ||||||
|  |   @spec changeset_error_map(Changeset.t()) :: %{atom() => [String.t()]} | ||||||
|  |   def changeset_error_map(changeset) do | ||||||
|  |     changeset | ||||||
|  |     |> Changeset.traverse_errors(fn error -> error |> translate_error() end) | ||||||
|  |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -1,16 +1,16 @@ | |||||||
| defmodule LokalWeb.ErrorView do | defmodule LokalWeb.ErrorView do | ||||||
|   use LokalWeb, :view |   use LokalWeb, :view | ||||||
|  |   import LokalWeb.Components.Topbar | ||||||
|  |   alias LokalWeb.{Endpoint, PageLive} | ||||||
|  |  | ||||||
|   # If you want to customize a particular status code |   def template_not_found(error_path, _assigns) do | ||||||
|   # for a certain format, you may uncomment below. |     error_string = | ||||||
|   # def render("500.html", _assigns) do |       case error_path do | ||||||
|   #   "Internal Server Error" |         "404.html" -> dgettext("errors", "Not found") | ||||||
|   # end |         "401.html" -> dgettext("errors", "Unauthorized") | ||||||
|  |         _ -> dgettext("errors", "Internal Server Error") | ||||||
|  |       end | ||||||
|  |  | ||||||
|   # By default, Phoenix returns the status message from |     render("error.html", %{error_string: error_string}) | ||||||
|   # the template name. For example, "404.html" becomes |  | ||||||
|   # "Not Found". |  | ||||||
|   def template_not_found(template, _assigns) do |  | ||||||
|     Phoenix.Controller.status_message_from_template(template) |  | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
| defmodule LokalWeb.UserConfirmationView do | defmodule LokalWeb.UserConfirmationView do | ||||||
|   use LokalWeb, :view |   use LokalWeb, :view | ||||||
|  |   alias Lokal.Accounts | ||||||
| end | end | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
| defmodule LokalWeb.UserResetPasswordView do | defmodule LokalWeb.UserResetPasswordView do | ||||||
|   use LokalWeb, :view |   use LokalWeb, :view | ||||||
|  |   alias Lokal.Accounts | ||||||
| end | end | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
| defmodule LokalWeb.UserSessionView do | defmodule LokalWeb.UserSessionView do | ||||||
|   use LokalWeb, :view |   use LokalWeb, :view | ||||||
|  |   alias Lokal.Accounts | ||||||
| end | end | ||||||
|   | |||||||
| @@ -1,7 +1,8 @@ | |||||||
| defmodule LokalWeb.ViewHelpers do | defmodule LokalWeb.ViewHelpers do | ||||||
|   @moduledoc """ |   @moduledoc """ | ||||||
|   Contains common helpers that can be used in liveviews and regular views. These |   Contains common helpers that can be used in liveviews and regular views. These | ||||||
|   are automatically imported into any Phoenix View using `use LokalWeb, :view` |   are automatically imported into any Phoenix View using `use LokalWeb, | ||||||
|  |   :view` | ||||||
|   """ |   """ | ||||||
|  |  | ||||||
|   import Phoenix.LiveView.Helpers |   import Phoenix.LiveView.Helpers | ||||||
| @@ -10,24 +11,42 @@ defmodule LokalWeb.ViewHelpers do | |||||||
|   Returns a <time> element that renders the naivedatetime in the user's local |   Returns a <time> element that renders the naivedatetime in the user's local | ||||||
|   timezone with Alpine.js |   timezone with Alpine.js | ||||||
|   """ |   """ | ||||||
|   @spec display_datetime(NaiveDateTime.t()) :: Phoenix.LiveView.Rendered.t() |   @spec display_datetime(NaiveDateTime.t() | nil) :: Phoenix.LiveView.Rendered.t() | ||||||
|  |   def display_datetime(nil), do: "" | ||||||
|  |  | ||||||
|   def display_datetime(datetime) do |   def display_datetime(datetime) do | ||||||
|     assigns = %{ |     assigns = %{ | ||||||
|       datetime: datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_iso8601(:extended) |       datetime: datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_iso8601(:extended) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ~H""" |     ~H""" | ||||||
|     <time |     <time datetime={@datetime} x-data={"{ | ||||||
|       datetime={@datetime} |  | ||||||
|       x-data={"{ |  | ||||||
|         date: |         date: | ||||||
|           Intl.DateTimeFormat([], {dateStyle: 'short', timeStyle: 'long'}) |           Intl.DateTimeFormat([], {dateStyle: 'short', timeStyle: 'long'}) | ||||||
|             .format(new Date(\"#{@datetime}\")) |             .format(new Date(\"#{@datetime}\")) | ||||||
|       }"} |       }"} x-text="date"> | ||||||
|       x-text="date" |  | ||||||
|     > |  | ||||||
|       <%= @datetime %> |       <%= @datetime %> | ||||||
|     </time> |     </time> | ||||||
|     """ |     """ | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   @doc """ | ||||||
|  |   Returns a <date> element that renders the Date in the user's local | ||||||
|  |   timezone with Alpine.js | ||||||
|  |   """ | ||||||
|  |   @spec display_date(Date.t() | nil) :: Phoenix.LiveView.Rendered.t() | ||||||
|  |   def display_date(nil), do: "" | ||||||
|  |  | ||||||
|  |   def display_date(date) do | ||||||
|  |     assigns = %{date: date |> Date.to_iso8601(:extended)} | ||||||
|  |  | ||||||
|  |     ~H""" | ||||||
|  |     <time datetime={@date} x-data={"{ | ||||||
|  |         date: | ||||||
|  |           Intl.DateTimeFormat([], {timeZone: 'Etc/UTC', dateStyle: 'short'}).format(new Date(\"#{@date}\")) | ||||||
|  |       }"} x-text="date"> | ||||||
|  |       <%= @date %> | ||||||
|  |     </time> | ||||||
|  |     """ | ||||||
|  |   end | ||||||
| end | end | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user