Skip to content

Commit

Permalink
Add email flow for ownership transfer
Browse files Browse the repository at this point in the history
  • Loading branch information
ukutaht committed Jun 15, 2021
1 parent d60a45e commit b8fd99d
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 171 deletions.
31 changes: 22 additions & 9 deletions lib/plausible_web/controllers/invitation_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ defmodule PlausibleWeb.InvitationController do

case Repo.transaction(multi) do
{:ok, _} ->
notify_invitation_acceptance(invitation)
notify_invitation_accepted(invitation)

conn
|> put_flash(:success, "You now have access to #{invitation.site.domain}")
Expand All @@ -60,26 +60,39 @@ defmodule PlausibleWeb.InvitationController do
Multi.update_all(multi, :prev_owner, prev_owner, set: [role: :admin])
end

defp notify_invitation_acceptance(invitation) do
PlausibleWeb.Email.invitation_accepted(invitation)
|> Plausible.Mailer.send_email()
end

def reject_invitation(conn, %{"invitation_id" => invitation_id}) do
invitation =
Repo.get_by!(Invitation, invitation_id: invitation_id)
|> Repo.preload([:site, :inviter])

Repo.delete!(invitation)

PlausibleWeb.Email.invitation_rejected(invitation)
|> Plausible.Mailer.send_email()
notify_invitation_rejected(invitation)

conn
|> put_flash(:success, "You have rejected the invitation to #{invitation.site.domain}")
|> redirect(to: "/sites")
end

defp notify_invitation_accepted(%Invitation{role: :owner} = invitation) do
PlausibleWeb.Email.ownership_transfer_accepted(invitation)
|> Plausible.Mailer.send_email()
end

defp notify_invitation_accepted(invitation) do
PlausibleWeb.Email.invitation_accepted(invitation)
|> Plausible.Mailer.send_email()
end

defp notify_invitation_rejected(%Invitation{role: :owner} = invitation) do
PlausibleWeb.Email.ownership_transfer_rejected(invitation)
|> Plausible.Mailer.send_email()
end

defp notify_invitation_rejected(invitation) do
PlausibleWeb.Email.invitation_rejected(invitation)
|> Plausible.Mailer.send_email()
end

def remove_invitation(conn, %{"invitation_id" => invitation_id}) do
invitation =
Repo.get_by!(Invitation, invitation_id: invitation_id)
Expand Down
66 changes: 41 additions & 25 deletions lib/plausible_web/controllers/site/membership_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,38 @@ defmodule PlausibleWeb.Site.MembershipController do
site = Sites.get_for_user!(conn.assigns[:current_user].id, site_domain)
user = Plausible.Auth.find_user_by(email: email)

invitation =
Invitation.new(%{
email: email,
role: role,
site_id: site.id,
inviter_id: conn.assigns[:current_user].id
})
|> Repo.insert!()
|> Repo.preload([:site, :inviter])

if user do
email_template = PlausibleWeb.Email.existing_user_invitation(invitation)

Plausible.Mailer.send_email(email_template)
if user && Sites.is_member?(user.id, site) do
msg = "Cannot send invite because #{user.email} is already a member of #{site.domain}"

render(conn, "invite_member_form.html",
error: msg,
site: site,
layout: {PlausibleWeb.LayoutView, "focus.html"}
)
else
email_template = PlausibleWeb.Email.new_user_invitation(invitation)
invitation =
Invitation.new(%{
email: email,
role: role,
site_id: site.id,
inviter_id: conn.assigns[:current_user].id
})
|> Repo.insert!()
|> Repo.preload([:site, :inviter])

email_template =
if user do
PlausibleWeb.Email.existing_user_invitation(invitation)
else
PlausibleWeb.Email.new_user_invitation(invitation)
end

Plausible.Mailer.send_email(email_template)
end

conn
|> put_flash(:success, "#{email} has been invited to #{site_domain} as a #{role}")
|> redirect(to: Routes.site_path(conn, :settings_general, site.domain))
conn
|> put_flash(:success, "#{email} has been invited to #{site_domain} as a #{role}")
|> redirect(to: Routes.site_path(conn, :settings_general, site.domain))
end
end

def transfer_ownership_form(conn, %{"website" => site_domain}) do
Expand All @@ -66,13 +75,20 @@ defmodule PlausibleWeb.Site.MembershipController do

def transfer_ownership(conn, %{"website" => site_domain, "email" => email}) do
site = Sites.get_for_user!(conn.assigns[:current_user].id, site_domain)
user = Plausible.Auth.find_user_by(email: email)

invitation =
Invitation.new(%{
email: email,
role: :owner,
site_id: site.id,
inviter_id: conn.assigns[:current_user].id
})
|> Repo.insert!()
|> Repo.preload([:site, :inviter])

Invitation.new(%{
email: email,
role: :owner,
site_id: site.id
})
|> Repo.insert!()
PlausibleWeb.Email.ownership_transfer_request(invitation, user)
|> Plausible.Mailer.send_email()

conn
|> put_flash(:success, "Site transfer request has been sent to #{email}")
Expand Down
3 changes: 3 additions & 0 deletions lib/plausible_web/controllers/site_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ defmodule PlausibleWeb.SiteController do
)
|> Repo.preload(:site)

invitation_site_ids = Enum.map(invitations, & &1.site.id)

{sites, pagination} =
Repo.paginate(
from(s in Plausible.Site,
join: sm in Plausible.Site.Membership,
on: sm.site_id == s.id,
where: sm.user_id == ^user.id,
where: s.id not in ^invitation_site_ids,
order_by: s.domain,
preload: [memberships: sm]
),
Expand Down
43 changes: 39 additions & 4 deletions lib/plausible_web/email.ex
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,6 @@ defmodule PlausibleWeb.Email do
|> tag("new-user-invitation")
|> subject("[Plausible Analytics] You've been invited to #{invitation.site.domain}")
|> render("new_user_invitation.html",
inviting_user: invitation.inviter,
site: invitation.site,
invitation: invitation
)
end
Expand All @@ -182,12 +180,21 @@ defmodule PlausibleWeb.Email do
|> tag("existing-user-invitation")
|> subject("[Plausible Analytics] You've been invited to #{invitation.site.domain}")
|> render("existing_user_invitation.html",
inviting_user: invitation.inviter,
site: invitation.site,
invitation: invitation
)
end

def ownership_transfer_request(invitation, new_owner_account) do
base_email()
|> to(invitation.email)
|> tag("ownership-transfer-request")
|> subject("[Plausible Analytics] Request to transfer ownership of #{invitation.site.domain}")
|> render("ownership_transfer_request.html",
invitation: invitation,
new_owner_account: new_owner_account
)
end

def invitation_accepted(invitation) do
base_email()
|> to(invitation.inviter.email)
Expand Down Expand Up @@ -216,6 +223,34 @@ defmodule PlausibleWeb.Email do
)
end

def ownership_transfer_accepted(invitation) do
base_email()
|> to(invitation.inviter.email)
|> tag("ownership-transfer-accepted")
|> subject(
"[Plausible Analytics] #{invitation.email} accepted the ownership transfer of #{
invitation.site.domain
}"
)
|> render("ownership_transfer_accepted.html",
invitation: invitation
)
end

def ownership_transfer_rejected(invitation) do
base_email()
|> to(invitation.inviter.email)
|> tag("ownership-transfer-rejected")
|> subject(
"[Plausible Analytics] #{invitation.email} rejected the ownership transfer of #{
invitation.site.domain
}"
)
|> render("ownership_transfer_rejected.html",
invitation: invitation
)
end

defp base_email() do
mailer_from = Application.get_env(:plausible, :mailer_email)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Hey,
<br /><br />
<%= @inviting_user.email %> has invited you to the <%= @site.domain %> site on Plausible Analytics.
<%= @invitation.inviter.email %> has invited you to the <%= @invitation.site.domain %> site on Plausible Analytics.
<%= link("Click here", to: Routes.site_path(PlausibleWeb.Endpoint, :index)) %> to view and respond to the invitation.
<br /><br />
Thanks,<br />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Hey,
<br /><br />
<%= @inviting_user.email %> has granted you access to the <%= @site.domain %> site on Plausible Analytics.
<%= @invitation.inviter.email %> has granted you access to the <%= @invitation.site.domain %> site on Plausible Analytics.
<%= link("Click here", to: Routes.auth_path(PlausibleWeb.Endpoint, :register_form), invitation: @invitation.invitation_id) %> to create your account.
<br /><br />
Plausible is a lightweight and open-source website analytics tool. We hope you like our simple and ethical approach to tracking website visitors.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Hey <%= user_salutation(@invitation.inviter) %>,
<br /><br />
<%= @invitation.email %> has accepted the ownership transfer of <%= @invitation.site.domain %>. They will be responsible for billing of it going
forward and your role has been changed to <b>admin</b>.
<%= link("Click here", to: Routes.site_path(PlausibleWeb.Endpoint, :settings_general, @invitation.site.domain)) %> to view site settings.
<br /><br />
Thanks,<br />
Uku and Marko<br />
--<br />
<%= plausible_url() %><br />
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Hey <%= user_salutation(@invitation.inviter) %>,
<br /><br />
<%= @invitation.email %> has rejected the ownership transfer of <%= @invitation.site.domain %>.
<%= link("Click here", to: Routes.site_path(PlausibleWeb.Endpoint, :settings_general, @invitation.site.domain)) %> to view site settings.
<br /><br />
Thanks,<br />
Uku and Marko<br />
--<br />
<%= plausible_url() %><br />
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Hey,
<br /><br />
<%= @invitation.inviter.email %> has request to transfer the ownership of <%= @invitation.site.domain %> site on Plausible Analytics to you.
<%= if @new_owner_account do %>
<%= link("Click here", to: Routes.site_path(PlausibleWeb.Endpoint, :index)) %> to view and respond to the invitation.
<% else %>
<%= link("Click here", to: Routes.auth_path(PlausibleWeb.Endpoint, :register_form), invitation: @invitation.invitation_id) %> to create your account.
<br /><br />
Plausible is a lightweight and open-source website analytics tool. We hope you like our simple and ethical approach to tracking website visitors.
<% end %>
<br /><br />
Thanks,<br />
Uku and Marko<br />
--<br />
<%= plausible_url() %><br />
29 changes: 19 additions & 10 deletions test/plausible_web/controllers/invitation_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,25 @@ defmodule PlausibleWeb.Site.InvitationControllerTest do
)
end

test "ownership transfer - notifies the original inviter with a different email", %{
conn: conn,
user: user
} do
inviter = insert(:user)
site = insert(:site)

invitation =
insert(:invitation, site_id: site.id, inviter: inviter, email: user.email, role: :owner)

post(conn, "/sites/invitations/#{invitation.invitation_id}/accept")

assert_email_delivered_with(
to: [nil: inviter.email],
subject:
"[Plausible Analytics] #{user.email} accepted the ownership transfer of #{site.domain}"
)
end

test "ownership transfer - downgrades previous owner to admin", %{conn: conn, user: user} do
old_owner = insert(:user)
site = insert(:site, members: [old_owner])
Expand Down Expand Up @@ -116,14 +135,4 @@ defmodule PlausibleWeb.Site.InvitationControllerTest do
)
end
end

describe "GET /sites/:website/transfer-ownership" do
test "shows the form", %{conn: conn, user: user} do
site = insert(:site, members: [user])

conn = get(conn, "/sites/#{site.domain}/transfer-ownership")

assert html_response(conn, 200) =~ "Transfer ownership"
end
end
end
Loading

0 comments on commit b8fd99d

Please sign in to comment.