From 77abf1e17a55393acdcc7175b38b55b61c3c0802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Sun, 23 Jun 2024 03:58:20 +0000 Subject: [PATCH 01/17] Added invite confirmation + little bit of logic, no test, no cleanup --- lib/nerves_hub/accounts.ex | 3 +- .../controllers/account_controller.ex | 106 ++++++++++++++++-- lib/nerves_hub_web/router.ex | 1 + .../account/invite_existing.html.heex | 11 ++ 4 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 lib/nerves_hub_web/templates/account/invite_existing.html.heex diff --git a/lib/nerves_hub/accounts.ex b/lib/nerves_hub/accounts.ex index e78f8dcf6..f10fd29e8 100644 --- a/lib/nerves_hub/accounts.ex +++ b/lib/nerves_hub/accounts.ex @@ -443,7 +443,8 @@ defmodule NervesHub.Accounts do def add_or_invite_to_org(%{"email" => email} = params, org) do case get_user_by_email(email) do {:error, :not_found} -> invite(params, org) - {:ok, user} -> add_org_user(org, user, %{role: params["role"]}) + {:ok, user} -> invite(params, org) + # add_org_user(org, user, %{role: params["role"]}) end end diff --git a/lib/nerves_hub_web/controllers/account_controller.ex b/lib/nerves_hub_web/controllers/account_controller.ex index 83b355ae4..cb66e35ca 100644 --- a/lib/nerves_hub_web/controllers/account_controller.ex +++ b/lib/nerves_hub_web/controllers/account_controller.ex @@ -56,13 +56,37 @@ defmodule NervesHubWeb.AccountController do def invite(conn, %{"token" => token} = _) do with {:ok, invite} <- Accounts.get_valid_invite(token), {:ok, org} <- Accounts.get_org(invite.org_id) do - render( - conn, - "invite.html", - changeset: %Changeset{data: invite}, - org: org, - token: token - ) + case Map.has_key?(conn.assigns, :user) && !is_nil(conn.assigns.user) do # QUESTION: Should this be here raw or in a method somewhere else? + true -> + if invite.email == conn.assigns.user.email do + render( + conn, + "invite_existing.html", # QUESTION: Should this be a separate template or the same one with conditional rendering? + changeset: %Changeset{data: invite}, + org: org, + token: token + ) + else + conn + |> put_flash(:error, "Invite not intended for the current user") + |> redirect(to: "/") + end + false -> + case Accounts.get_user_by_email(invite.email) do + {:ok, recipient} -> # Invites for existing users + conn + |> put_flash(:error, "You must be logged in to accept this invite") + |> redirect(to: "/login") + {:error, :not_found} -> # Invites for new users + render( + conn, + "invite.html", + changeset: %Changeset{data: invite}, + org: org, + token: token + ) + end + end else _ -> conn @@ -90,6 +114,36 @@ defmodule NervesHubWeb.AccountController do end end + def accept_invite_existing(conn, %{"token" => token} = _) do + case Map.has_key?(conn.assigns, :user) && !is_nil(conn.assigns.user) do # QUESTION rep: Should this be here raw or in a method somewhere else? + true -> + with {:ok, invite} <- Accounts.get_valid_invite(token), + {:ok, org} <- Accounts.get_org(invite.org_id), + {:ok, _} <- Accounts.user_invite_recipient?(invite, conn.assigns.user) do + + _accept_invite_existing(conn, token, invite, org) + else + {:error, :invite_not_found} -> + conn + |> put_flash(:error, "Invalid or expired invite") + |> redirect(to: "/") + + {:error, :org_not_found} -> + conn + |> put_flash(:error, "Invalid org") + |> redirect(to: "/") + {:error, :invite_not_for_user} -> + conn + |> put_flash(:error, "Invite not intended for the current user") + |> redirect(to: "/") + end + false -> + conn + |> put_flash(:error, "You must be logged in to accept this invite") + |> redirect(to: "/") + end + end + defp _accept_invite(conn, token, clean_params, invite, org) do with {:ok, new_org_user} <- Accounts.create_user_from_invite(invite, org, clean_params) do # Now let everyone in the organization - except the new guy - @@ -127,4 +181,42 @@ defmodule NervesHubWeb.AccountController do ) end end + + defp _accept_invite_existing(conn, token, invite, org) do + with {:ok, new_org_user} <- Accounts.accept_invite(invite, org) do + # Now let everyone in the organization - except the new guy - + # know about this new user. + + # TODO: Fix this - We don't have the instigating user in the conn + # anymore, and the new user is not always the instigator. + instigator = + case conn.assigns do + %{user: %{username: username}} -> username + _ -> nil + end + + email = + SwooshEmail.tell_org_user_added( + org, + Accounts.get_org_users(org), + instigator, + new_org_user.user + ) + + SwooshMailer.deliver(email) + + conn + |> put_flash(:info, "Organization successfully joined") + |> redirect(to: "/") + else + {:error, %Changeset{} = changeset} -> + render( + conn, + "invite_existing.html", + changeset: changeset, + org: org, + token: token + ) + end + end end diff --git a/lib/nerves_hub_web/router.ex b/lib/nerves_hub_web/router.ex index 89cb8d630..f7511dfd3 100644 --- a/lib/nerves_hub_web/router.ex +++ b/lib/nerves_hub_web/router.ex @@ -198,6 +198,7 @@ defmodule NervesHubWeb.Router do get("/invite/:token", AccountController, :invite) post("/invite/:token", AccountController, :accept_invite) + post("/invite_existing/:token", AccountController, :accept_invite_existing) end scope "/", NervesHubWeb do diff --git a/lib/nerves_hub_web/templates/account/invite_existing.html.heex b/lib/nerves_hub_web/templates/account/invite_existing.html.heex new file mode 100644 index 000000000..0ab545744 --- /dev/null +++ b/lib/nerves_hub_web/templates/account/invite_existing.html.heex @@ -0,0 +1,11 @@ +
+

+ Organization Invitation +

+
<%= gettext("You have been invited to join to the %{organization_name} organization", organization_name: @org.name) %>
+ + <%= form_for @changeset, Routes.account_path(@conn, :accept_invite_existing, @token), [method: "post", class: "form-page"], fn f -> %> +
<%= error_tag(f, :email) %>
+ <%= submit("Accept Invitation", class: "btn btn-primary btn-lg w-100") %> + <% end %> +
\ No newline at end of file From 5739e7d585e4af9bb8b8f53f2959aca0695f8047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Sun, 23 Jun 2024 04:01:09 +0000 Subject: [PATCH 02/17] Avoid temporary warnings for draft pr --- lib/nerves_hub/accounts.ex | 2 +- lib/nerves_hub_web/controllers/account_controller.ex | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/nerves_hub/accounts.ex b/lib/nerves_hub/accounts.ex index f10fd29e8..bef455daf 100644 --- a/lib/nerves_hub/accounts.ex +++ b/lib/nerves_hub/accounts.ex @@ -443,7 +443,7 @@ defmodule NervesHub.Accounts do def add_or_invite_to_org(%{"email" => email} = params, org) do case get_user_by_email(email) do {:error, :not_found} -> invite(params, org) - {:ok, user} -> invite(params, org) + {:ok, _user} -> invite(params, org) # add_org_user(org, user, %{role: params["role"]}) end end diff --git a/lib/nerves_hub_web/controllers/account_controller.ex b/lib/nerves_hub_web/controllers/account_controller.ex index cb66e35ca..f5668312d 100644 --- a/lib/nerves_hub_web/controllers/account_controller.ex +++ b/lib/nerves_hub_web/controllers/account_controller.ex @@ -73,7 +73,7 @@ defmodule NervesHubWeb.AccountController do end false -> case Accounts.get_user_by_email(invite.email) do - {:ok, recipient} -> # Invites for existing users + {:ok, _recipient} -> # Invites for existing users conn |> put_flash(:error, "You must be logged in to accept this invite") |> redirect(to: "/login") @@ -181,7 +181,6 @@ defmodule NervesHubWeb.AccountController do ) end end - defp _accept_invite_existing(conn, token, invite, org) do with {:ok, new_org_user} <- Accounts.accept_invite(invite, org) do # Now let everyone in the organization - except the new guy - From 088d95133429bff012eb44dc745b74815c1bdd1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Sun, 23 Jun 2024 04:05:39 +0000 Subject: [PATCH 03/17] Missing Account methods from ghost repo --- lib/nerves_hub/accounts.ex | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lib/nerves_hub/accounts.ex b/lib/nerves_hub/accounts.ex index bef455daf..a12e8b6a7 100644 --- a/lib/nerves_hub/accounts.ex +++ b/lib/nerves_hub/accounts.ex @@ -521,6 +521,41 @@ defmodule NervesHub.Accounts do end) end + @spec accept_invite(Invite.t(), Org.t()) :: + {:ok, OrgUser.t()} | {:error, Ecto.Changeset.t()} + def accept_invite(invite, org) do + Repo.transaction(fn -> + with {:ok, user} <- get_user_by_email(invite.email), + {:ok, user} <- add_org_user(org, user, %{role: invite.role}), + {:ok, _invite} <- set_invite_accepted(invite) do + # Repo.transaction will wrap this in an {:ok, user} + user + else + {:error, error} -> Repo.rollback(error) + end + end) + end + + @spec user_invite_recipient?(Invite.t(), User.t()) :: + {:ok, Invite.t()} | {:error, :invite_not_for_user} + def user_invite_recipient?(invite, user) do + if invite.email == user.email do + {:ok, invite} + else + {:error, :invite_not_for_user} + end + end + + @spec user_invite_recipient?(Invite.t(), User.t()) :: + {:ok, Invite.t()} | {:error, :invite_not_for_user} + def user_invite_recipient?(invite, user) do + if invite.email == user.email do + {:ok, invite} + else + {:error, :invite_not_for_user} + end + end + @spec update_user(User.t(), map) :: {:ok, User.t()} | {:error, Changeset.t()} From 3b26f7a50f9c7ada2dc6504c872227584657ba98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Sun, 23 Jun 2024 04:08:39 +0000 Subject: [PATCH 04/17] Copy paste typo --- lib/nerves_hub/accounts.ex | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/nerves_hub/accounts.ex b/lib/nerves_hub/accounts.ex index a12e8b6a7..bd6033084 100644 --- a/lib/nerves_hub/accounts.ex +++ b/lib/nerves_hub/accounts.ex @@ -546,16 +546,6 @@ defmodule NervesHub.Accounts do end end - @spec user_invite_recipient?(Invite.t(), User.t()) :: - {:ok, Invite.t()} | {:error, :invite_not_for_user} - def user_invite_recipient?(invite, user) do - if invite.email == user.email do - {:ok, invite} - else - {:error, :invite_not_for_user} - end - end - @spec update_user(User.t(), map) :: {:ok, User.t()} | {:error, Changeset.t()} From f90824ddb3d8a0daf97e9cfa1e83a503665ccad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Sun, 23 Jun 2024 06:31:12 +0200 Subject: [PATCH 05/17] new formating --- lib/nerves_hub/accounts.ex | 7 +- .../controllers/account_controller.ex | 67 +++++++++++-------- .../account/invite_existing.html.heex | 7 +- 3 files changed, 47 insertions(+), 34 deletions(-) diff --git a/lib/nerves_hub/accounts.ex b/lib/nerves_hub/accounts.ex index bd6033084..047c2e228 100644 --- a/lib/nerves_hub/accounts.ex +++ b/lib/nerves_hub/accounts.ex @@ -442,8 +442,11 @@ defmodule NervesHub.Accounts do | {:error, Changeset.t()} def add_or_invite_to_org(%{"email" => email} = params, org) do case get_user_by_email(email) do - {:error, :not_found} -> invite(params, org) - {:ok, _user} -> invite(params, org) + {:error, :not_found} -> + invite(params, org) + + {:ok, _user} -> + invite(params, org) # add_org_user(org, user, %{role: params["role"]}) end end diff --git a/lib/nerves_hub_web/controllers/account_controller.ex b/lib/nerves_hub_web/controllers/account_controller.ex index f5668312d..b6ec88682 100644 --- a/lib/nerves_hub_web/controllers/account_controller.ex +++ b/lib/nerves_hub_web/controllers/account_controller.ex @@ -56,37 +56,43 @@ defmodule NervesHubWeb.AccountController do def invite(conn, %{"token" => token} = _) do with {:ok, invite} <- Accounts.get_valid_invite(token), {:ok, org} <- Accounts.get_org(invite.org_id) do - case Map.has_key?(conn.assigns, :user) && !is_nil(conn.assigns.user) do # QUESTION: Should this be here raw or in a method somewhere else? - true -> - if invite.email == conn.assigns.user.email do + # QUESTION: Should this be here raw or in a method somewhere else? + case Map.has_key?(conn.assigns, :user) && !is_nil(conn.assigns.user) do + true -> + if invite.email == conn.assigns.user.email do + render( + conn, + # QUESTION: Should this be a separate template or the same one with conditional rendering? + "invite_existing.html", + changeset: %Changeset{data: invite}, + org: org, + token: token + ) + else + conn + |> put_flash(:error, "Invite not intended for the current user") + |> redirect(to: "/") + end + + false -> + case Accounts.get_user_by_email(invite.email) do + # Invites for existing users + {:ok, _recipient} -> + conn + |> put_flash(:error, "You must be logged in to accept this invite") + |> redirect(to: "/login") + + # Invites for new users + {:error, :not_found} -> render( conn, - "invite_existing.html", # QUESTION: Should this be a separate template or the same one with conditional rendering? + "invite.html", changeset: %Changeset{data: invite}, org: org, token: token ) - else - conn - |> put_flash(:error, "Invite not intended for the current user") - |> redirect(to: "/") - end - false -> - case Accounts.get_user_by_email(invite.email) do - {:ok, _recipient} -> # Invites for existing users - conn - |> put_flash(:error, "You must be logged in to accept this invite") - |> redirect(to: "/login") - {:error, :not_found} -> # Invites for new users - render( - conn, - "invite.html", - changeset: %Changeset{data: invite}, - org: org, - token: token - ) - end - end + end + end else _ -> conn @@ -115,12 +121,12 @@ defmodule NervesHubWeb.AccountController do end def accept_invite_existing(conn, %{"token" => token} = _) do - case Map.has_key?(conn.assigns, :user) && !is_nil(conn.assigns.user) do # QUESTION rep: Should this be here raw or in a method somewhere else? + # QUESTION rep: Should this be here raw or in a method somewhere else? + case Map.has_key?(conn.assigns, :user) && !is_nil(conn.assigns.user) do true -> with {:ok, invite} <- Accounts.get_valid_invite(token), - {:ok, org} <- Accounts.get_org(invite.org_id), - {:ok, _} <- Accounts.user_invite_recipient?(invite, conn.assigns.user) do - + {:ok, org} <- Accounts.get_org(invite.org_id), + {:ok, _} <- Accounts.user_invite_recipient?(invite, conn.assigns.user) do _accept_invite_existing(conn, token, invite, org) else {:error, :invite_not_found} -> @@ -132,11 +138,13 @@ defmodule NervesHubWeb.AccountController do conn |> put_flash(:error, "Invalid org") |> redirect(to: "/") + {:error, :invite_not_for_user} -> conn |> put_flash(:error, "Invite not intended for the current user") |> redirect(to: "/") end + false -> conn |> put_flash(:error, "You must be logged in to accept this invite") @@ -181,6 +189,7 @@ defmodule NervesHubWeb.AccountController do ) end end + defp _accept_invite_existing(conn, token, invite, org) do with {:ok, new_org_user} <- Accounts.accept_invite(invite, org) do # Now let everyone in the organization - except the new guy - diff --git a/lib/nerves_hub_web/templates/account/invite_existing.html.heex b/lib/nerves_hub_web/templates/account/invite_existing.html.heex index 0ab545744..97221405e 100644 --- a/lib/nerves_hub_web/templates/account/invite_existing.html.heex +++ b/lib/nerves_hub_web/templates/account/invite_existing.html.heex @@ -2,10 +2,11 @@

Organization Invitation

+
<%= gettext("You have been invited to join to the %{organization_name} organization", organization_name: @org.name) %>
- + <%= form_for @changeset, Routes.account_path(@conn, :accept_invite_existing, @token), [method: "post", class: "form-page"], fn f -> %>
<%= error_tag(f, :email) %>
- <%= submit("Accept Invitation", class: "btn btn-primary btn-lg w-100") %> + <%= submit("Accept Invitation", class: "btn btn-primary btn-lg w-100") %> <% end %> - \ No newline at end of file + From 59b41bee75cafb1694a72a1c280c2890ac965da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Sun, 23 Jun 2024 16:38:42 +0200 Subject: [PATCH 06/17] heex reformat --- .../templates/account/invite_existing.html.heex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/nerves_hub_web/templates/account/invite_existing.html.heex b/lib/nerves_hub_web/templates/account/invite_existing.html.heex index 97221405e..67d9b44b4 100644 --- a/lib/nerves_hub_web/templates/account/invite_existing.html.heex +++ b/lib/nerves_hub_web/templates/account/invite_existing.html.heex @@ -2,11 +2,11 @@

Organization Invitation

- +
<%= gettext("You have been invited to join to the %{organization_name} organization", organization_name: @org.name) %>
- + <%= form_for @changeset, Routes.account_path(@conn, :accept_invite_existing, @token), [method: "post", class: "form-page"], fn f -> %>
<%= error_tag(f, :email) %>
- <%= submit("Accept Invitation", class: "btn btn-primary btn-lg w-100") %> + <%= submit("Accept Invitation", class: "btn btn-primary btn-lg w-100") %> <% end %> From c2cdabf60fedf77b1a6db03dcadb61d81c5d7deb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Thu, 27 Jun 2024 02:14:28 +0000 Subject: [PATCH 07/17] Simplified invite flow and merged both behaviours into a single route --- .../controllers/account_controller.ex | 47 +++++-------------- lib/nerves_hub_web/router.ex | 1 - 2 files changed, 12 insertions(+), 36 deletions(-) diff --git a/lib/nerves_hub_web/controllers/account_controller.ex b/lib/nerves_hub_web/controllers/account_controller.ex index b6ec88682..9f3f42eb7 100644 --- a/lib/nerves_hub_web/controllers/account_controller.ex +++ b/lib/nerves_hub_web/controllers/account_controller.ex @@ -101,12 +101,21 @@ defmodule NervesHubWeb.AccountController do end end - def accept_invite(conn, %{"user" => user_params, "token" => token} = _) do - clean_params = whitelist(user_params, [:password, :username]) + def accept_invite(conn, %{"token" => token} = params) do with {:ok, invite} <- Accounts.get_valid_invite(token), {:ok, org} <- Accounts.get_org(invite.org_id) do - _accept_invite(conn, token, clean_params, invite, org) + case Accounts.get_user_by_email(invite.email) do + {:ok, _recipient} -> + _accept_invite_existing(conn, token, invite, org) + + {:error, :not_found} -> + clean_params = + params + |> Map.fetch!("user") + |> whitelist([:password, :username]) + _accept_invite(conn, token, clean_params, invite, org) + end else {:error, :invite_not_found} -> conn @@ -120,38 +129,6 @@ defmodule NervesHubWeb.AccountController do end end - def accept_invite_existing(conn, %{"token" => token} = _) do - # QUESTION rep: Should this be here raw or in a method somewhere else? - case Map.has_key?(conn.assigns, :user) && !is_nil(conn.assigns.user) do - true -> - with {:ok, invite} <- Accounts.get_valid_invite(token), - {:ok, org} <- Accounts.get_org(invite.org_id), - {:ok, _} <- Accounts.user_invite_recipient?(invite, conn.assigns.user) do - _accept_invite_existing(conn, token, invite, org) - else - {:error, :invite_not_found} -> - conn - |> put_flash(:error, "Invalid or expired invite") - |> redirect(to: "/") - - {:error, :org_not_found} -> - conn - |> put_flash(:error, "Invalid org") - |> redirect(to: "/") - - {:error, :invite_not_for_user} -> - conn - |> put_flash(:error, "Invite not intended for the current user") - |> redirect(to: "/") - end - - false -> - conn - |> put_flash(:error, "You must be logged in to accept this invite") - |> redirect(to: "/") - end - end - defp _accept_invite(conn, token, clean_params, invite, org) do with {:ok, new_org_user} <- Accounts.create_user_from_invite(invite, org, clean_params) do # Now let everyone in the organization - except the new guy - diff --git a/lib/nerves_hub_web/router.ex b/lib/nerves_hub_web/router.ex index f7511dfd3..89cb8d630 100644 --- a/lib/nerves_hub_web/router.ex +++ b/lib/nerves_hub_web/router.ex @@ -198,7 +198,6 @@ defmodule NervesHubWeb.Router do get("/invite/:token", AccountController, :invite) post("/invite/:token", AccountController, :accept_invite) - post("/invite_existing/:token", AccountController, :accept_invite_existing) end scope "/", NervesHubWeb do From 2e75472a5a4fcdec4d61ed5168475b247e68a3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Thu, 27 Jun 2024 02:14:52 +0000 Subject: [PATCH 08/17] View uses new route --- lib/nerves_hub_web/templates/account/invite_existing.html.heex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/nerves_hub_web/templates/account/invite_existing.html.heex b/lib/nerves_hub_web/templates/account/invite_existing.html.heex index 67d9b44b4..b2afec9b6 100644 --- a/lib/nerves_hub_web/templates/account/invite_existing.html.heex +++ b/lib/nerves_hub_web/templates/account/invite_existing.html.heex @@ -5,7 +5,7 @@
<%= gettext("You have been invited to join to the %{organization_name} organization", organization_name: @org.name) %>
- <%= form_for @changeset, Routes.account_path(@conn, :accept_invite_existing, @token), [method: "post", class: "form-page"], fn f -> %> + <%= form_for @changeset, Routes.account_path(@conn, :accept_invite, @token), [method: "post", class: "form-page"], fn f -> %>
<%= error_tag(f, :email) %>
<%= submit("Accept Invitation", class: "btn btn-primary btn-lg w-100") %> <% end %> From a51f377121c9b8fd21ff75a5f7ba074297e445c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Thu, 27 Jun 2024 02:48:11 +0000 Subject: [PATCH 09/17] Added helper functions for invites and popup on login --- lib/nerves_hub/accounts.ex | 8 ++++++++ .../controllers/account_controller.ex | 16 ++++++++++++++++ .../controllers/home_controller.ex | 6 +++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/nerves_hub/accounts.ex b/lib/nerves_hub/accounts.ex index 047c2e228..e821e2839 100644 --- a/lib/nerves_hub/accounts.ex +++ b/lib/nerves_hub/accounts.ex @@ -494,6 +494,14 @@ defmodule NervesHub.Accounts do |> Repo.all() end + @spec get_invites_for_user(User.t()) :: [Invite.t()] + def get_invites_for_user(user) do + Invite + |> where([i], i.email == ^user.email) + |> where([i], i.accepted == false) + |> Repo.all() + end + def delete_invite(org, token) do Invite |> where([i], i.org_id == ^org.id) diff --git a/lib/nerves_hub_web/controllers/account_controller.ex b/lib/nerves_hub_web/controllers/account_controller.ex index 9f3f42eb7..7a64f2c61 100644 --- a/lib/nerves_hub_web/controllers/account_controller.ex +++ b/lib/nerves_hub_web/controllers/account_controller.ex @@ -204,4 +204,20 @@ defmodule NervesHubWeb.AccountController do ) end end + + def maybe_show_invites(conn) do + case Map.has_key?(conn.assigns, :user) && !is_nil(conn.assigns.user) do + true -> + case conn.assigns.user + |> Accounts.get_invites_for_user() do + [] -> + conn + invites -> + conn + |> put_flash(:info, "You have " <> (length(invites) |> Integer.to_string()) <>" pending invite" <> (if length(invites) > 1, do: "s", else: "") <> ".") + end + false -> + conn + end + end end diff --git a/lib/nerves_hub_web/controllers/home_controller.ex b/lib/nerves_hub_web/controllers/home_controller.ex index d78745f28..580d54893 100644 --- a/lib/nerves_hub_web/controllers/home_controller.ex +++ b/lib/nerves_hub_web/controllers/home_controller.ex @@ -1,10 +1,14 @@ defmodule NervesHubWeb.HomeController do use NervesHubWeb, :controller + alias NervesHubWeb.AccountController + def index(conn, _params) do case Map.has_key?(conn.assigns, :user) && !is_nil(conn.assigns.user) do true -> - render(conn, "index.html") + conn + |> AccountController.maybe_show_invites() + |> render("index.html") false -> redirect(conn, to: Routes.session_path(conn, :new)) From 6a027b296e556257debc607d9f3083ae94106384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Thu, 27 Jun 2024 03:04:58 +0000 Subject: [PATCH 10/17] pre format --- lib/nerves_hub_web/controllers/account_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/nerves_hub_web/controllers/account_controller.ex b/lib/nerves_hub_web/controllers/account_controller.ex index 7a64f2c61..bad56b2bd 100644 --- a/lib/nerves_hub_web/controllers/account_controller.ex +++ b/lib/nerves_hub_web/controllers/account_controller.ex @@ -214,7 +214,7 @@ defmodule NervesHubWeb.AccountController do conn invites -> conn - |> put_flash(:info, "You have " <> (length(invites) |> Integer.to_string()) <>" pending invite" <> (if length(invites) > 1, do: "s", else: "") <> ".") + |> put_flash(:info, "You have " <> (length(invites) |> Integer.to_string()) <> " pending invite" <> (if length(invites) > 1, do: "s", else: "") <> ".") end false -> conn From 0608d3a6daf5a1c576ce73a69f695958e24c5aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Thu, 27 Jun 2024 03:06:40 +0000 Subject: [PATCH 11/17] mix format --- .../controllers/account_controller.ex | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/lib/nerves_hub_web/controllers/account_controller.ex b/lib/nerves_hub_web/controllers/account_controller.ex index bad56b2bd..04f6e68b3 100644 --- a/lib/nerves_hub_web/controllers/account_controller.ex +++ b/lib/nerves_hub_web/controllers/account_controller.ex @@ -102,20 +102,20 @@ defmodule NervesHubWeb.AccountController do end def accept_invite(conn, %{"token" => token} = params) do - with {:ok, invite} <- Accounts.get_valid_invite(token), {:ok, org} <- Accounts.get_org(invite.org_id) do - case Accounts.get_user_by_email(invite.email) do - {:ok, _recipient} -> - _accept_invite_existing(conn, token, invite, org) + case Accounts.get_user_by_email(invite.email) do + {:ok, _recipient} -> + _accept_invite_existing(conn, token, invite, org) - {:error, :not_found} -> - clean_params = - params - |> Map.fetch!("user") - |> whitelist([:password, :username]) - _accept_invite(conn, token, clean_params, invite, org) - end + {:error, :not_found} -> + clean_params = + params + |> Map.fetch!("user") + |> whitelist([:password, :username]) + + _accept_invite(conn, token, clean_params, invite, org) + end else {:error, :invite_not_found} -> conn @@ -209,13 +209,20 @@ defmodule NervesHubWeb.AccountController do case Map.has_key?(conn.assigns, :user) && !is_nil(conn.assigns.user) do true -> case conn.assigns.user - |> Accounts.get_invites_for_user() do + |> Accounts.get_invites_for_user() do [] -> conn + invites -> conn - |> put_flash(:info, "You have " <> (length(invites) |> Integer.to_string()) <> " pending invite" <> (if length(invites) > 1, do: "s", else: "") <> ".") + |> put_flash( + :info, + "You have " <> + (length(invites) |> Integer.to_string()) <> + " pending invite" <> if(length(invites) > 1, do: "s", else: "") <> "." + ) end + false -> conn end From 1e414cf00db4e37fe7ed6edd7a1601329c3e2211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Sun, 7 Jul 2024 23:45:22 +0000 Subject: [PATCH 12/17] Notes and fixed message --- lib/nerves_hub_web/controllers/account_controller.ex | 2 +- lib/nerves_hub_web/controllers/org_controller.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/nerves_hub_web/controllers/account_controller.ex b/lib/nerves_hub_web/controllers/account_controller.ex index 04f6e68b3..c51466334 100644 --- a/lib/nerves_hub_web/controllers/account_controller.ex +++ b/lib/nerves_hub_web/controllers/account_controller.ex @@ -219,7 +219,7 @@ defmodule NervesHubWeb.AccountController do :info, "You have " <> (length(invites) |> Integer.to_string()) <> - " pending invite" <> if(length(invites) > 1, do: "s", else: "") <> "." + " pending invite" <> if(length(invites) > 1, do: "s", else: "") <> " to organizations." ) end diff --git a/lib/nerves_hub_web/controllers/org_controller.ex b/lib/nerves_hub_web/controllers/org_controller.ex index 836f5b3a5..a5332297b 100644 --- a/lib/nerves_hub_web/controllers/org_controller.ex +++ b/lib/nerves_hub_web/controllers/org_controller.ex @@ -77,7 +77,7 @@ defmodule NervesHubWeb.OrgController do |> put_flash(:info, "User has been invited") |> redirect(to: Routes.org_user_path(conn, :index, conn.assigns.org.name)) - {:ok, %OrgUser{}} -> + {:ok, %OrgUser{}} -> # TODO: (Beltran) review this case statement and delete unused matches SwooshEmail.org_user_created(invite_params["email"], org) |> SwooshMailer.deliver() From ff899160fa196ad3be1219f1e31ed16a12331e2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Mon, 8 Jul 2024 00:38:29 +0000 Subject: [PATCH 13/17] Working link in phoenix flash --- lib/nerves_hub_web/controllers/account_controller.ex | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/nerves_hub_web/controllers/account_controller.ex b/lib/nerves_hub_web/controllers/account_controller.ex index c51466334..d4ab36f79 100644 --- a/lib/nerves_hub_web/controllers/account_controller.ex +++ b/lib/nerves_hub_web/controllers/account_controller.ex @@ -6,6 +6,8 @@ defmodule NervesHubWeb.AccountController do alias NervesHub.Accounts.SwooshEmail alias NervesHub.SwooshMailer + import Phoenix.HTML.Link + def edit(conn, _params) do conn |> render( @@ -216,10 +218,12 @@ defmodule NervesHubWeb.AccountController do invites -> conn |> put_flash( - :info, + :info, [ "You have " <> (length(invites) |> Integer.to_string()) <> - " pending invite" <> if(length(invites) > 1, do: "s", else: "") <> " to organizations." + " pending invite" <> if(length(invites) > 1, do: "s", else: "") <> " to organizations. ", + link("View pending invites.", to: "/org/" <> conn.assigns.user <> "/invites") + ] ) end From 08e6b61ed67a0c75f586e5c75d971b85cf4392fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Mon, 8 Jul 2024 09:32:17 +0000 Subject: [PATCH 14/17] mix format --- .../controllers/account_controller.ex | 35 ++++++++++++++++--- lib/nerves_hub_web/router.ex | 2 ++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/lib/nerves_hub_web/controllers/account_controller.ex b/lib/nerves_hub_web/controllers/account_controller.ex index d4ab36f79..87d09b28c 100644 --- a/lib/nerves_hub_web/controllers/account_controller.ex +++ b/lib/nerves_hub_web/controllers/account_controller.ex @@ -36,6 +36,27 @@ defmodule NervesHubWeb.AccountController do end end + def invites(conn, params) do + case Accounts.get_user_by_username(params["username"]) do + {:ok, user} -> + case Accounts.get_invites_for_user(user) do + [] -> + conn + |> put_flash(:info, "No pending invites") + |> redirect(to: Routes.account_path(conn, :edit, user.username)) + + invites -> + conn + |> render("invites.html", invites: invites) + end + + {:error, :not_found} -> + conn + |> put_flash(:error, "User not found") + |> redirect(to: "/") + end + end + def update(conn, params) do cleaned = params["user"] @@ -218,11 +239,15 @@ defmodule NervesHubWeb.AccountController do invites -> conn |> put_flash( - :info, [ - "You have " <> - (length(invites) |> Integer.to_string()) <> - " pending invite" <> if(length(invites) > 1, do: "s", else: "") <> " to organizations. ", - link("View pending invites.", to: "/org/" <> conn.assigns.user <> "/invites") + :info, + [ + "You have " <> + (length(invites) |> Integer.to_string()) <> + " pending invite" <> + if(length(invites) > 1, do: "s", else: "") <> " to organizations. ", + link("Click here to view pending invites.", + to: "/org/" <> conn.assigns.user.username <> "/invites" + ) ] ) end diff --git a/lib/nerves_hub_web/router.ex b/lib/nerves_hub_web/router.ex index 89cb8d630..28bab2ca6 100644 --- a/lib/nerves_hub_web/router.ex +++ b/lib/nerves_hub_web/router.ex @@ -198,6 +198,8 @@ defmodule NervesHubWeb.Router do get("/invite/:token", AccountController, :invite) post("/invite/:token", AccountController, :accept_invite) + + get("/invites/:user_name", AccountController, :invites) end scope "/", NervesHubWeb do From d6bceaccb37a5de37449c99a8f3e70bedd96b224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Mon, 8 Jul 2024 10:02:06 +0000 Subject: [PATCH 15/17] format and fixed url in router --- lib/nerves_hub_web/controllers/org_controller.ex | 3 ++- lib/nerves_hub_web/router.ex | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/nerves_hub_web/controllers/org_controller.ex b/lib/nerves_hub_web/controllers/org_controller.ex index a5332297b..917086700 100644 --- a/lib/nerves_hub_web/controllers/org_controller.ex +++ b/lib/nerves_hub_web/controllers/org_controller.ex @@ -77,7 +77,8 @@ defmodule NervesHubWeb.OrgController do |> put_flash(:info, "User has been invited") |> redirect(to: Routes.org_user_path(conn, :index, conn.assigns.org.name)) - {:ok, %OrgUser{}} -> # TODO: (Beltran) review this case statement and delete unused matches + # TODO: (Beltran) review this case statement and delete unused matches + {:ok, %OrgUser{}} -> SwooshEmail.org_user_created(invite_params["email"], org) |> SwooshMailer.deliver() diff --git a/lib/nerves_hub_web/router.ex b/lib/nerves_hub_web/router.ex index 28bab2ca6..79d1affd3 100644 --- a/lib/nerves_hub_web/router.ex +++ b/lib/nerves_hub_web/router.ex @@ -199,7 +199,11 @@ defmodule NervesHubWeb.Router do get("/invite/:token", AccountController, :invite) post("/invite/:token", AccountController, :accept_invite) - get("/invites/:user_name", AccountController, :invites) + scope "/invites/:user_name" do + pipe_through([:logged_in]) + + get("/", AccountController, :invites) + end end scope "/", NervesHubWeb do From e993a157379ed50b821f015f6112ebda9629c559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Mon, 8 Jul 2024 22:16:02 +0000 Subject: [PATCH 16/17] Fixing failed merge --- lib/nerves_hub/accounts.ex | 4 +-- .../controllers/account_controller.ex | 29 +++++++++++++++++++ .../controllers/home_controller.ex | 16 ++-------- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/lib/nerves_hub/accounts.ex b/lib/nerves_hub/accounts.ex index 93f6fe445..e781c8033 100644 --- a/lib/nerves_hub/accounts.ex +++ b/lib/nerves_hub/accounts.ex @@ -425,10 +425,10 @@ defmodule NervesHub.Accounts do def add_or_invite_to_org(%{"email" => email} = params, org, invited_by) do case get_user_by_email(email) do {:error, :not_found} -> - invite(params, org) + invite(params, org, invited_by) {:ok, _user} -> - invite(params, org) + invite(params, org, invited_by) # add_org_user(org, user, %{role: params["role"]}) end end diff --git a/lib/nerves_hub_web/controllers/account_controller.ex b/lib/nerves_hub_web/controllers/account_controller.ex index d40291960..1563f01f0 100644 --- a/lib/nerves_hub_web/controllers/account_controller.ex +++ b/lib/nerves_hub_web/controllers/account_controller.ex @@ -136,4 +136,33 @@ defmodule NervesHubWeb.AccountController do ) end end + + def maybe_show_invites(conn) do + case Map.has_key?(conn.assigns, :user) && !is_nil(conn.assigns.user) do + true -> + case conn.assigns.user + |> Accounts.get_invites_for_user() do + [] -> + conn + + invites -> + conn + |> put_flash( + :info, + [ + "You have " <> + (length(invites) |> Integer.to_string()) <> + " pending invite" <> + if(length(invites) > 1, do: "s", else: "") <> " to organizations. ", + link("Click here to view pending invites.", + to: "/org/" <> conn.assigns.user.username <> "/invites" + ) + ] + ) + end + + false -> + conn + end + end end diff --git a/lib/nerves_hub_web/controllers/home_controller.ex b/lib/nerves_hub_web/controllers/home_controller.ex index 6f6fbf553..68899eff4 100644 --- a/lib/nerves_hub_web/controllers/home_controller.ex +++ b/lib/nerves_hub_web/controllers/home_controller.ex @@ -4,18 +4,8 @@ defmodule NervesHubWeb.HomeController do alias NervesHubWeb.AccountController def index(conn, _params) do - case Map.has_key?(conn.assigns, :user) && !is_nil(conn.assigns.user) do - true -> - conn - |> AccountController.maybe_show_invites() - |> render("index.html") - - false -> - redirect(conn, to: ~p"/orgs") - end - end - - def error(_conn, _params) do - raise "Error" + conn + |> AccountController.maybe_show_invites() + |> redirect(to: ~p"/orgs") end end From b3b4ce0ca817e935cfcb7bd2f1be3aca39e7a9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beltr=C3=A1n=20Aceves?= Date: Mon, 8 Jul 2024 22:27:01 +0000 Subject: [PATCH 17/17] More lost changes --- .../controllers/account_controller.ex | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/nerves_hub_web/controllers/account_controller.ex b/lib/nerves_hub_web/controllers/account_controller.ex index 1563f01f0..59b91eeb9 100644 --- a/lib/nerves_hub_web/controllers/account_controller.ex +++ b/lib/nerves_hub_web/controllers/account_controller.ex @@ -31,7 +31,7 @@ defmodule NervesHubWeb.AccountController do conn.assigns.user |> Accounts.update_user(cleaned) |> case do - {:ok, user} -> + {:ok, _user} -> conn |> put_flash(:info, "Account successfully created, login below") |> redirect(to: "/login") @@ -165,4 +165,15 @@ defmodule NervesHubWeb.AccountController do conn end end + + defp registrations_allowed(conn, _options) do + if Application.get_env(:nerves_hub, :open_for_registrations) do + conn + else + conn + |> put_flash(:info, "Please contact support for an invite to this platform.") + |> redirect(to: "/login") + |> halt() + end + end end