implemented optional 'allow_registration' config, with first registered user being pre-confirmed Admin, registration unavailable after that point if allow_registration: :false

This commit is contained in:
Adam Piontek 2022-08-14 09:14:42 -04:00
parent ea74a89078
commit f27df8d676
22 changed files with 395 additions and 244 deletions

View file

@ -1,12 +1,14 @@
const togglerBtn = document.getElementById('navbarSupportedContentToggler'); const togglerBtn = document.getElementById('navbarSupportedContentToggler');
const navbarContent = document.getElementById('navbarSupportedContent'); const navbarContent = document.getElementById('navbarSupportedContent');
navbarContent.addEventListener('show.bs.collapse', () => { if (navbarContent != null) {
console.log('opening navbar content'); navbarContent.addEventListener('show.bs.collapse', () => {
togglerBtn.classList.toggle('is-active'); console.log('opening navbar content');
}); togglerBtn.classList.toggle('is-active');
});
navbarContent.addEventListener('hide.bs.collapse', () => {
console.log('closing navbar content'); navbarContent.addEventListener('hide.bs.collapse', () => {
togglerBtn.classList.toggle('is-active'); console.log('closing navbar content');
}); togglerBtn.classList.toggle('is-active');
});
}

View file

@ -16,7 +16,8 @@ config :shift73k,
config :shift73k, :app_global_vars, config :shift73k, :app_global_vars,
time_zone: "America/New_York", time_zone: "America/New_York",
mailer_reply_to: "reply_to@example.com", mailer_reply_to: "reply_to@example.com",
mailer_from: "app_name@example.com" mailer_from: "app_name@example.com",
allow_registration: :true
# Configures the endpoint # Configures the endpoint
config :shift73k, Shift73kWeb.Endpoint, config :shift73k, Shift73kWeb.Endpoint,

View file

@ -108,6 +108,13 @@ defmodule Shift73k.Accounts do
""" """
def register_user(attrs) do def register_user(attrs) do
# If attrs has atom keys, convert to string
# If attrs don't include role, put default role
attrs =
attrs
|> Map.new(fn {k, v} -> {to_string(k), v} end)
|> Map.put_new("role", registration_role())
%User{} %User{}
|> User.registration_changeset(attrs) |> User.registration_changeset(attrs)
|> Repo.insert() |> Repo.insert()

View file

@ -1,6 +1,6 @@
defmodule Shift73kWeb.UserLive.Registration do defmodule Shift73kWeb.UserLive.Registration do
use Shift73kWeb, :live_view use Shift73kWeb, :live_view
alias Shift73k.Repo
alias Shift73k.Accounts alias Shift73k.Accounts
alias Shift73k.Accounts.User alias Shift73k.Accounts.User
@ -20,9 +20,7 @@ defmodule Shift73kWeb.UserLive.Registration do
user_id: nil, user_id: nil,
user_return_to: Map.get(session, "user_return_to", "/"), user_return_to: Map.get(session, "user_return_to", "/"),
messages: [ messages: [
success: "Welcome! Your new account has been created, and you've been logged in.", success: "Welcome! Your new account has been created, and you've been logged in."
info:
"Some features may be unavailable until you confirm your email address. Check your inbox for instructions."
] ]
} }
end end
@ -35,19 +33,33 @@ defmodule Shift73kWeb.UserLive.Registration do
@impl true @impl true
def handle_event("save", %{"user" => user_params}, socket) do def handle_event("save", %{"user" => user_params}, socket) do
is_first_user = !Repo.exists?(User)
user_params user_params
|> Map.put("role", Accounts.registration_role())
|> Accounts.register_user() |> Accounts.register_user()
|> case do |> case do
{:ok, user} -> {:ok, user} ->
{:ok, _, %Swoosh.Email{} = _captured_email} = # If this is the first user, we just confirm them
Accounts.deliver_user_confirmation_instructions( if is_first_user do
user, user |> User.confirm_changeset() |> Repo.update()
&Routes.user_confirmation_url(socket, :confirm, &1) else
) # Otherwise, all new users require email confirmation so we wend instructions
{:ok, _, %Swoosh.Email{} = _captured_email} =
Accounts.deliver_user_confirmation_instructions(
user,
&Routes.user_confirmation_url(socket, :confirm, &1)
)
end
login_params =
if is_first_user do
socket.assigns.login_params
else
put_in(socket.assigns.login_params, [:messages, :info], "Some features may be unavailable until you confirm your email address. Check your inbox for instructions.")
end
|> put_in([:user_id], user.id)
socket socket
|> assign(login_params: %{socket.assigns.login_params | user_id: user.id}) |> assign(login_params: login_params)
|> assign(trigger_submit: true) |> assign(trigger_submit: true)
|> live_noreply() |> live_noreply()

View file

@ -6,7 +6,7 @@
</h2> </h2>
<p class="lead">Create an account to manage your work shifts with us.</p> <p class="lead">Create an account to manage your work shifts with us.</p>
<%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, novalidate: true, id: "reg_form"], fn f -> %> <.form let={f} for={@changeset} phx-change="validate" phx-submit="save" novalidate id="reg_form">
<%= label f, :email, class: "form-label" %> <%= label f, :email, class: "form-label" %>
<div class="inner-addon left-addon mb-3" phx-feedback-for={input_id(f, :email)}> <div class="inner-addon left-addon mb-3" phx-feedback-for={input_id(f, :email)}>
@ -45,7 +45,7 @@
%> %>
</div> </div>
<% end %> </.form>
<p> <p>
<%= link "Log in", to: Routes.user_session_path(@socket, :new) %> | <%= link "Log in", to: Routes.user_session_path(@socket, :new) %> |

View file

@ -4,6 +4,9 @@ defmodule Shift73kWeb.UserLive.ResetPassword do
alias Shift73k.Accounts alias Shift73k.Accounts
alias Shift73k.Accounts.User alias Shift73k.Accounts.User
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true)
@app_allow_registration @app_vars[:allow_registration]
@impl true @impl true
def mount(_params, session, socket) do def mount(_params, session, socket) do
user = Accounts.get_user!(session["user_id"]) user = Accounts.get_user!(session["user_id"])
@ -37,4 +40,6 @@ defmodule Shift73kWeb.UserLive.ResetPassword do
|> assign(changeset: changeset)} |> assign(changeset: changeset)}
end end
end end
def allow_registration, do: @app_allow_registration
end end

View file

@ -45,7 +45,9 @@
<% end %> <% end %>
<p class="mt-3 is-pulled-right"> <p class="mt-3 is-pulled-right">
<%= link "Register", to: Routes.user_registration_path(@socket, :new) %> | <%= if allow_registration() do %>
<%= link "Register", to: Routes.user_registration_path(@socket, :new) %> |
<% end %>
<%= link "Log in", to: Routes.user_session_path(@socket, :new) %> <%= link "Log in", to: Routes.user_session_path(@socket, :new) %>
</p> </p>

View file

@ -0,0 +1,35 @@
defmodule Shift73kWeb.EnsureAllowRegistrationPlug do
@moduledoc """
This plug ensures that there is at least one known User.
"""
import Plug.Conn
import Phoenix.Controller
alias Shift73k.Repo
alias Shift73k.Accounts.User
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true)
@app_allow_registration @app_vars[:allow_registration]
@doc false
@spec init(any()) :: any()
def init(config), do: config
@doc false
@spec call(Conn.t(), atom() | [atom()]) :: Conn.t()
def call(conn, _opts) do
# If there aren't even any users, or registration is allowed
if !Repo.exists?(User) || @app_allow_registration do
# We will allow registration
conn
else
# Otherwise,
# if app is configured to not allow registration,
# and there is a user,
# then we redirect to root URL
conn
|> redirect(to: "/")
|> halt()
end
end
end

View file

@ -27,8 +27,7 @@ defmodule Shift73kWeb.EnsureRolePlug do
def call(conn, roles) do def call(conn, roles) do
user_token = get_session(conn, :user_token) user_token = get_session(conn, :user_token)
(user_token && (user_token && Accounts.get_user_by_session_token(user_token))
Accounts.get_user_by_session_token(user_token))
|> has_role?(roles) |> has_role?(roles)
|> maybe_halt(conn) |> maybe_halt(conn)
end end

View file

@ -0,0 +1,30 @@
defmodule Shift73kWeb.EnsureUserExistPlug do
@moduledoc """
This plug ensures that there is at least one known User.
"""
import Plug.Conn
import Phoenix.Controller
alias Shift73k.Repo
alias Shift73k.Accounts.User
alias Shift73kWeb.Router.Helpers, as: Routes
@doc false
@spec init(any()) :: any()
def init(config), do: config
@doc false
@spec call(Conn.t(), atom() | [atom()]) :: Conn.t()
def call(conn, _opts) do
# If there aren't even any users,
if !Repo.exists?(User) do
# We're just going to redirect to registration
conn
|> redirect(to: Routes.user_registration_path(conn, :new))
|> halt()
else
# Otherwise we proceed as normal
conn
end
end
end

View file

@ -2,27 +2,37 @@ defmodule Shift73kWeb.Router do
use Shift73kWeb, :router use Shift73kWeb, :router
import Shift73kWeb.UserAuth import Shift73kWeb.UserAuth
alias Shift73kWeb.EnsureRolePlug alias Shift73kWeb.EnsureRolePlug
alias Shift73kWeb.EnsureUserExistPlug
alias Shift73kWeb.EnsureAllowRegistrationPlug
pipeline :browser do pipeline :browser do
plug(:accepts, ["html"]) plug :accepts, ["html"]
plug(:fetch_session) plug :fetch_session
plug(:fetch_live_flash) plug :fetch_live_flash
plug(:put_root_layout, {Shift73kWeb.LayoutView, :root}) plug :put_root_layout, {Shift73kWeb.LayoutView, :root}
plug(:protect_from_forgery) plug :protect_from_forgery
plug(:put_secure_browser_headers) plug :put_secure_browser_headers
plug(:fetch_current_user) plug :fetch_current_user
end end
pipeline :user do pipeline :ensure_role_user do
plug(EnsureRolePlug, [:admin, :manager, :user]) plug EnsureRolePlug, [:admin, :manager, :user]
end end
pipeline :manager do pipeline :ensure_user_exist do
plug(EnsureRolePlug, [:admin, :manager]) plug EnsureUserExistPlug
end end
pipeline :admin do pipeline :ensure_allow_registration do
plug(EnsureRolePlug, :admin) plug EnsureAllowRegistrationPlug
end
pipeline :ensure_role_manager do
plug EnsureRolePlug, [:admin, :manager]
end
pipeline :ensure_role_admin do
plug EnsureRolePlug, :admin
end end
# Enables the Swoosh mailbox preview in development. # Enables the Swoosh mailbox preview in development.
@ -38,49 +48,54 @@ defmodule Shift73kWeb.Router do
end end
scope "/", Shift73kWeb do scope "/", Shift73kWeb do
pipe_through([:browser]) pipe_through([:browser, :ensure_user_exist])
get("/", Redirector, to: "/assign") get "/", Redirector, to: "/assign"
end end
scope "/", Shift73kWeb do scope "/", Shift73kWeb do
pipe_through([:browser, :redirect_if_user_is_authenticated]) pipe_through [:browser, :redirect_if_user_is_authenticated, :ensure_allow_registration]
get "/users/register", UserRegistrationController, :new
end
scope "/", Shift73kWeb do
pipe_through [:browser, :redirect_if_user_is_authenticated, :ensure_user_exist]
# session routes, irrelevant if user is authenticated # session routes, irrelevant if user is authenticated
get("/users/register", UserRegistrationController, :new) get "/users/log_in", UserSessionController, :new
get("/users/log_in", UserSessionController, :new) post "/users/log_in", UserSessionController, :create
post("/users/log_in", UserSessionController, :create) get "/users/reset_password", UserResetPasswordController, :new
get("/users/reset_password", UserResetPasswordController, :new) post "/users/reset_password", UserResetPasswordController, :create
post("/users/reset_password", UserResetPasswordController, :create) get "/users/reset_password/:token", UserResetPasswordController, :edit
get("/users/reset_password/:token", UserResetPasswordController, :edit)
end end
scope "/", Shift73kWeb do scope "/", Shift73kWeb do
pipe_through([:browser, :require_authenticated_user]) pipe_through [:browser, :require_authenticated_user]
# user settings (change email, password, calendar week start, etc) # user settings (change email, password, calendar week start, etc)
live("/users/settings", UserLive.Settings, :edit) live "/users/settings", UserLive.Settings, :edit
# confirm email by token # confirm email by token
get("/users/settings/confirm_email/:token", UserSettingsController, :confirm_email) get "/users/settings/confirm_email/:token", UserSettingsController, :confirm_email
end end
scope "/", Shift73kWeb do scope "/", Shift73kWeb do
pipe_through([:browser]) pipe_through [:browser, :ensure_user_exist]
# session paths # session paths
delete("/users/log_out", UserSessionController, :delete) delete "/users/log_out", UserSessionController, :delete
get("/users/force_logout", UserSessionController, :force_logout) get "/users/force_logout", UserSessionController, :force_logout
get("/users/confirm", UserConfirmationController, :new) get "/users/confirm", UserConfirmationController, :new
post("/users/confirm", UserConfirmationController, :create) post "/users/confirm", UserConfirmationController, :create
get("/users/confirm/:token", UserConfirmationController, :confirm) get "/users/confirm/:token", UserConfirmationController, :confirm
# ics/ical route for user's shifts # ics/ical route for user's shifts
get("/ics/:slug", UserShiftsIcsController, :index) get "/ics/:slug", UserShiftsIcsController, :index
end end
scope "/", Shift73kWeb do scope "/", Shift73kWeb do
pipe_through([:browser, :require_authenticated_user, :user]) pipe_through [:browser, :require_authenticated_user, :ensure_role_user]
live "/templates", ShiftTemplateLive.Index, :index live "/templates", ShiftTemplateLive.Index, :index
live "/templates/new", ShiftTemplateLive.Index, :new live "/templates/new", ShiftTemplateLive.Index, :new
@ -98,16 +113,16 @@ defmodule Shift73kWeb.Router do
end end
# scope "/", Shift73kWeb do # scope "/", Shift73kWeb do
# pipe_through([:browser, :require_authenticated_user, :admin]) # pipe_through([:browser, :require_authenticated_user, :ensure_role_admin])
# end # end
# Users Management # Users Management
scope "/users", Shift73kWeb do scope "/users", Shift73kWeb do
pipe_through([:browser, :require_authenticated_user, :manager, :require_email_confirmed]) pipe_through [:browser, :require_authenticated_user, :ensure_role_manager, :require_email_confirmed]
live("/", UserManagementLive.Index, :index) live "/", UserManagementLive.Index, :index
live("/new", UserManagementLive.Index, :new) live "/new", UserManagementLive.Index, :new
live("/edit/:id", UserManagementLive.Index, :edit) live "/edit/:id", UserManagementLive.Index, :edit
# resources "/", UserManagementController, only: [:new, :create, :edit, :update] # resources "/", UserManagementController, only: [:new, :create, :edit, :update]
end end
end end

View file

@ -8,79 +8,63 @@
<% end %> <% end %>
</h1> </h1>
<%= if @current_user do %> <%# If there's a current user,
OR if there are users & we allow registration,
THEN we will show a full menu configuration %>
<%= if @current_user || (Repo.exists?(User) && allow_registration()) do %>
<button class="hamburger hamburger--squeeze collapsed navbar-toggler" id="navbarSupportedContentToggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <button class="hamburger hamburger--squeeze collapsed navbar-toggler" id="navbarSupportedContentToggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="hamburger-box d-flex"> <span class="hamburger-box d-flex">
<span class="hamburger-inner"></span> <span class="hamburger-inner"></span>
</span> </span>
</button> </button>
<% else %>
<%= link nav_link_opts(@conn, to: Routes.user_session_path(@conn, :new), class: "btn btn-outline-light d-block d-lg-none") do %>
<i class="bi bi-door-open me-1"></i> Log in
<% end %>
<% end %>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<%# nav LEFT items %> <%# nav LEFT items %>
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
<%#= if @current_user do %> </ul>
<%# <li class="nav-item"> %>
<%#= link nav_link_opts(@conn, to: Routes.shift_template_index_path(@conn, :index), class: "nav-link") do %>
<%#= icon_div @conn, "bi-clock-history", [class: "icon baseline me-1"] %>
<%# Templates %>
<%# end %>
<%# </li> %>
<%# end %>
<%# normal navbar link example %>
<%# <li class="nav-item"> %>
<%#= link "Properties", nav_link_opts(@conn, to: Routes.property_index_path(@conn, :index), class: "nav-link") %>
<%# </li> %>
<%# ACTIVE page link example %> <%# nav RIGHT items %>
<%# <li class="nav-item"> <ul class="navbar-nav">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li> %>
<%# DISABLED page link example %> <%= if @current_user do %>
<%# <li class="nav-item">
<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
</li> %>
<%# normal dropdown menu example %> <%= render "navbar/_shifts_menu.html", assigns %>
<%# <li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownExample" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdownExample">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
</ul>
</li> %>
</ul> <%= render "navbar/_user_menu.html", assigns %>
<% else %>
<%# nav RIGHT items %> <%= render "navbar/_nouser_menu.html", assigns %>
<ul class="navbar-nav">
<%= if @current_user do %>
<%= render "navbar/_shifts_menu.html", assigns %>
<%= render "navbar/_user_menu.html", assigns %>
<% else %>
<%= link nav_link_opts(@conn, to: Routes.user_session_path(@conn, :new), class: "btn btn-outline-light d-none d-lg-block") do %>
<i class="bi bi-door-open me-1"></i> Log in
<% end %> <% end %>
</ul>
</div>
<%# If there's no current user,
AND:
There are no users -- [REGISTER]
OR no registration allowed -- [LOG IN] %>
<%= else %>
<%= if !Repo.exists?(User) || allow_registration() do %>
<%= link nav_link_opts(@conn, to: Routes.user_registration_path(@conn, :new), class: "btn btn-outline-light") do %>
<i class="bi bi-journal-plus"></i> Register
<% end %> <% end %>
<% else %>
<%= link nav_link_opts(@conn, to: Routes.user_session_path(@conn, :new), class: "btn btn-outline-light") do %>
<i class="bi bi-door-open"></i> Log in
<% end %>
<% end %>
</ul> <% end %>
</div>
</div> </div>
</nav> </nav>

View file

@ -0,0 +1,23 @@
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" id="navbarDropdownUserMenu" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-circle me-1"></i> Hello?
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownUserMenu">
<li>
<%= link nav_link_opts(@conn, to: Routes.user_registration_path(@conn, :new), class: "dropdown-item") do %>
<i class="bi bi-journal-plus me-1"></i> Register
<% end %>
</li>
<li>
<%= link nav_link_opts(@conn, to: Routes.user_session_path(@conn, :new), class: "dropdown-item") do %>
<i class="bi bi-door-open me-1"></i> Log in
<% end %>
</li>
</ul>
</li>

View file

@ -1,7 +1,7 @@
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" id="navbarDropdownUserMenu" data-bs-toggle="dropdown" aria-expanded="false"> <a href="#" class="nav-link dropdown-toggle" id="navbarDropdownUserMenu" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-circle me-1"></i> <%= @current_user && "Hello!" || "Hello?" %> <i class="bi bi-person-circle me-1"></i> Hello!
</a> </a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownUserMenu"> <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownUserMenu">

View file

@ -29,6 +29,9 @@
<% end %> <% end %>
<p> <p>
<%= if allow_registration() do %>
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
<% end %>
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> | <%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
<%= link "Log in", to: Routes.user_session_path(@conn, :new) %> <%= link "Log in", to: Routes.user_session_path(@conn, :new) %>
</p> </p>

View file

@ -27,7 +27,9 @@
<% end %> <% end %>
<p> <p>
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> | <%= if allow_registration() do %>
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
<% end %>
<%= link "Log in", to: Routes.user_session_path(@conn, :new) %> <%= link "Log in", to: Routes.user_session_path(@conn, :new) %>
</p> </p>

View file

@ -49,7 +49,9 @@
<% end %> <% end %>
<p> <p>
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> | <%= if allow_registration() do %>
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
<% end %>
<%= link "Forgot your password?", to: Routes.user_reset_password_path(@conn, :new) %> <%= link "Forgot your password?", to: Routes.user_reset_password_path(@conn, :new) %>
</p> </p>

View file

@ -1,9 +1,12 @@
defmodule Shift73kWeb.LayoutView do defmodule Shift73kWeb.LayoutView do
use Shift73kWeb, :view use Shift73kWeb, :view
alias Shift73k.Repo
alias Shift73k.Accounts.User alias Shift73k.Accounts.User
alias Shift73kWeb.Roles alias Shift73kWeb.Roles
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true)
@app_allow_registration @app_vars[:allow_registration]
# With a Vite.js-based workflow, we will import different asset files in development # With a Vite.js-based workflow, we will import different asset files in development
# and in production builds. Therefore, we will need a way to conditionally render # and in production builds. Therefore, we will need a way to conditionally render
# <script> tags based on Mix environment. However, since Mix is not available in # <script> tags based on Mix environment. However, since Mix is not available in
@ -11,6 +14,8 @@ defmodule Shift73kWeb.LayoutView do
@env Mix.env() # remember value at compile time @env Mix.env() # remember value at compile time
def dev_env?, do: @env == :dev def dev_env?, do: @env == :dev
def allow_registration, do: @app_allow_registration
def nav_link_opts(conn, opts) do def nav_link_opts(conn, opts) do
case Keyword.get(opts, :to) == Phoenix.Controller.current_path(conn) do case Keyword.get(opts, :to) == Phoenix.Controller.current_path(conn) do
false -> opts false -> opts

View file

@ -1,4 +1,9 @@
defmodule Shift73kWeb.UserConfirmationView do defmodule Shift73kWeb.UserConfirmationView do
use Shift73kWeb, :view use Shift73kWeb, :view
alias Shift73k.Accounts.User alias Shift73k.Accounts.User
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true)
@app_allow_registration @app_vars[:allow_registration]
def allow_registration, do: @app_allow_registration
end end

View file

@ -1,4 +1,9 @@
defmodule Shift73kWeb.UserResetPasswordView do defmodule Shift73kWeb.UserResetPasswordView do
use Shift73kWeb, :view use Shift73kWeb, :view
alias Shift73k.Accounts.User alias Shift73k.Accounts.User
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true)
@app_allow_registration @app_vars[:allow_registration]
def allow_registration, do: @app_allow_registration
end end

View file

@ -1,4 +1,9 @@
defmodule Shift73kWeb.UserSessionView do defmodule Shift73kWeb.UserSessionView do
use Shift73kWeb, :view use Shift73kWeb, :view
alias Shift73k.Accounts.User alias Shift73k.Accounts.User
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true)
@app_allow_registration @app_vars[:allow_registration]
def allow_registration, do: @app_allow_registration
end end

View file

@ -14,141 +14,150 @@ alias Shift73k.Repo
alias Shift73k.Accounts alias Shift73k.Accounts
alias Shift73k.Accounts.User alias Shift73k.Accounts.User
############################################################################
## INSERTING MOCK USER DATA
{:ok, _admin} = if Mix.env() == :dev do
Accounts.register_user(%{
email: "admin@company.com",
password: "123456789abC",
password_confirmation: "123456789abC",
role: Accounts.registration_role()
})
{:ok, _user_1} = if System.get_env("ECTO_SEED_DB") do
Accounts.register_user(%{
email: "user1@company.com",
password: "123456789abC",
password_confirmation: "123456789abC",
role: Accounts.registration_role()
})
{:ok, _user_2} = ############################################################################
Accounts.register_user(%{ ## INSERTING MOCK USER DATA
email: "user2@company.com",
password: "123456789abC",
password_confirmation: "123456789abC",
role: Accounts.registration_role()
})
# if Mix.env() == :dev do {:ok, _admin} =
this_path = Path.dirname(__ENV__.file) Accounts.register_user(%{
users_json = Path.join(this_path, "MOCK_DATA_users.json") email: "admin@company.com",
password: "123456789abC",
password_confirmation: "123456789abC",
role: Accounts.registration_role()
})
count_to_take = 15 {:ok, _user_1} =
Accounts.register_user(%{
email: "user1@company.com",
password: "123456789abC",
password_confirmation: "123456789abC",
role: Accounts.registration_role()
})
mock_users = users_json |> File.read!() |> Jason.decode!() |> Enum.take_random(count_to_take) {:ok, _user_2} =
Accounts.register_user(%{
email: "user2@company.com",
password: "123456789abC",
password_confirmation: "123456789abC",
role: Accounts.registration_role()
})
extra_mock_users = ~s([ # if Mix.env() == :dev do
{"email":"adam@73k.us","password":"adamadamA1","role":"admin","inserted_at":"2018-12-14T01:01:01Z","confirmed_at":true}, this_path = Path.dirname(__ENV__.file)
{"email":"kat@73k.us","password":"katkatA1","role":"manager","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":true}, users_json = Path.join(this_path, "MOCK_DATA_users.json")
{"email":"babka@73k.us","password":"Babka2020","role":"user","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":false},
{"email":"malcolm@73k.us","password":"Malc2018","role":"user","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":false},
{"email":"casio@73k.us","password":"Casio2011","role":"user","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":false}
])
# for random week_start_at values count_to_take = 15
[head | tail] = Shift73k.weekdays()
week_starts = [head | Enum.drop(tail, 4)]
mock_users = mock_users = users_json |> File.read!() |> Jason.decode!() |> Enum.take_random(count_to_take)
extra_mock_users
|> Jason.decode!()
|> Stream.concat(mock_users)
|> Enum.map(fn e ->
add_dt = NaiveDateTime.from_iso8601!(e["inserted_at"])
%{ extra_mock_users = ~s([
email: e["email"], {"email":"adam@73k.us","password":"adamadamA1","role":"admin","inserted_at":"2018-12-14T01:01:01Z","confirmed_at":true},
role: String.to_existing_atom(e["role"]), {"email":"kat@73k.us","password":"katkatA1","role":"manager","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":true},
hashed_password: Bcrypt.hash_pwd_salt(e["password"]), {"email":"babka@73k.us","password":"Babka2020","role":"user","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":false},
week_start_at: Enum.at(week_starts, Enum.random(0..2)), {"email":"malcolm@73k.us","password":"Malc2018","role":"user","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":false},
calendar_slug: Ecto.UUID.generate(), {"email":"casio@73k.us","password":"Casio2011","role":"user","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":false}
inserted_at: add_dt, ])
updated_at: add_dt,
confirmed_at: (e["confirmed_at"] && NaiveDateTime.add(add_dt, 300, :second)) || nil
}
end)
Repo.insert_all(User, mock_users) # for random week_start_at values
# end [head | tail] = Shift73k.weekdays()
week_starts = [head | Enum.drop(tail, 4)]
##### mock_users =
# shift tepmlates extra_mock_users
alias Shift73k.Shifts.Templates.ShiftTemplate |> Jason.decode!()
|> Stream.concat(mock_users)
|> Enum.map(fn e ->
add_dt = NaiveDateTime.from_iso8601!(e["inserted_at"])
shifts_json = Path.join(this_path, "MOCK_DATA_shift-templates.json") %{
mock_shifts = shifts_json |> File.read!() |> Jason.decode!() email: e["email"],
role: String.to_existing_atom(e["role"]),
hashed_password: Bcrypt.hash_pwd_salt(e["password"]),
week_start_at: Enum.at(week_starts, Enum.random(0..2)),
calendar_slug: Ecto.UUID.generate(),
inserted_at: add_dt,
updated_at: add_dt,
confirmed_at: (e["confirmed_at"] && NaiveDateTime.add(add_dt, 300, :second)) || nil
}
end)
Repo.insert_all(User, mock_users)
# end
#####
# shift tepmlates
alias Shift73k.Shifts.Templates.ShiftTemplate
shifts_json = Path.join(this_path, "MOCK_DATA_shift-templates.json")
mock_shifts = shifts_json |> File.read!() |> Jason.decode!()
time_from_mock = fn mock_time ->
case String.length(mock_time) do
4 -> Time.from_iso8601!("T0#{mock_time}:00")
5 -> Time.from_iso8601!("T#{mock_time}:00")
end
end
seconds_day = 86_400
seconds_days_14 = seconds_day * 14
seconds_half_day = Integer.floor_div(seconds_day, 2)
for user <- Accounts.list_users() do
user_shifts =
mock_shifts
|> Enum.take_random(:rand.uniform(15) + 5)
|> Enum.map(fn e ->
seconds_to_add = :rand.uniform(seconds_days_14) + seconds_half_day
add_dt = NaiveDateTime.add(user.inserted_at, seconds_to_add)
time_start = time_from_mock.(e["time_start"])
shift_len_min = e["length_minutes"] || 0
shift_length = e["length_hours"] * 60 * 60 + shift_len_min * 60
time_end = Time.add(time_start, shift_length) |> Time.truncate(:second)
%{
subject: e["subject"],
description: e["description"],
location: e["location"],
time_zone: Tzdata.zone_list() |> Enum.random(),
time_start: time_start,
time_end: time_end,
user_id: user.id,
inserted_at: add_dt,
updated_at: add_dt
}
end)
Repo.insert_all(ShiftTemplate, user_shifts)
end
#####
# insert shifts for each user?
alias Shift73k.Shifts
alias Shift73k.Shifts.Templates
for user <- Accounts.list_users() do
# build a date range for the time from 120 days ago to 120 days from now
today = Date.utc_today()
date_range = Date.range(Date.add(today, -120), Date.add(today, 120))
# get 3 random shift templates for user
st_list = Templates.list_shift_templates_by_user(user.id) |> Enum.take_random(3)
for st <- st_list do
days_to_schedule = Enum.take_random(date_range, 47)
shift_data = ShiftTemplate.attrs(st)
days_to_schedule
|> Stream.map(&Map.put(shift_data, :date, &1))
|> Enum.map(&Repo.timestamp/1)
|> Shifts.create_multiple()
end
end
time_from_mock = fn mock_time ->
case String.length(mock_time) do
4 -> Time.from_iso8601!("T0#{mock_time}:00")
5 -> Time.from_iso8601!("T#{mock_time}:00")
end
end
seconds_day = 86_400
seconds_days_14 = seconds_day * 14
seconds_half_day = Integer.floor_div(seconds_day, 2)
for user <- Accounts.list_users() do
user_shifts =
mock_shifts
|> Enum.take_random(:rand.uniform(15) + 5)
|> Enum.map(fn e ->
seconds_to_add = :rand.uniform(seconds_days_14) + seconds_half_day
add_dt = NaiveDateTime.add(user.inserted_at, seconds_to_add)
time_start = time_from_mock.(e["time_start"])
shift_len_min = e["length_minutes"] || 0
shift_length = e["length_hours"] * 60 * 60 + shift_len_min * 60
time_end = Time.add(time_start, shift_length) |> Time.truncate(:second)
%{
subject: e["subject"],
description: e["description"],
location: e["location"],
time_zone: Tzdata.zone_list() |> Enum.random(),
time_start: time_start,
time_end: time_end,
user_id: user.id,
inserted_at: add_dt,
updated_at: add_dt
}
end)
Repo.insert_all(ShiftTemplate, user_shifts)
end
#####
# insert shifts for each user?
alias Shift73k.Shifts
alias Shift73k.Shifts.Templates
for user <- Accounts.list_users() do
# build a date range for the time from 120 days ago to 120 days from now
today = Date.utc_today()
date_range = Date.range(Date.add(today, -120), Date.add(today, 120))
# get 3 random shift templates for user
st_list = Templates.list_shift_templates_by_user(user.id) |> Enum.take_random(3)
for st <- st_list do
days_to_schedule = Enum.take_random(date_range, 47)
shift_data = ShiftTemplate.attrs(st)
days_to_schedule
|> Stream.map(&Map.put(shift_data, :date, &1))
|> Enum.map(&Repo.timestamp/1)
|> Shifts.create_multiple()
end end
end end