analytics/lib/plausible_web/controllers/invitation_controller.ex
Uku Taht e71de6dc1f
Invitations (#1122)
* Invite existing user to a site

* Add invitation flow for non-existing users

* Accept and reject invitations

* Use invitation flow for existing users

* Locking mechanism for sites

* Authorization for site settings

* Show usage based on site ownership

* Add ability to remove members from a site

* Do not show settings link to viewer roles

* Ability to remove invitations

* Remove `Plausible.Sites.count_for/1`

* Fix tests

* Do not show the trial banner after the trial

* Correct trial emails

* Transfer ownership

* Send invitation email to existing user

* Add invitation email flows

* Add plug for role-based authorization

* Rename AuthorizeStatsPlug -> AuthorizeSiteAccess

* Add email flow for ownership transfer

* Fix URLs in emails

* Fix small copy issues

* Make 'People' its own section in site settings

* Notify user via email if their access has been removed

* Check site lock status when invitation is accepted

* Check lock status when user subscribes

* Make sure only admins and owners can create shared links

* Changelog

* Add LockSites to daily cron

* Clean invitations after 48 hours

* Add notices about expiry

* Add invitation expired page

* Add doc link
2021-06-16 15:00:07 +03:00

109 lines
3.2 KiB
Elixir

defmodule PlausibleWeb.InvitationController do
use PlausibleWeb, :controller
use Plausible.Repo
alias Ecto.Multi
alias Plausible.Auth.Invitation
alias Plausible.Site.Membership
plug PlausibleWeb.RequireAccountPlug
def accept_invitation(conn, %{"invitation_id" => invitation_id}) do
invitation =
Repo.get_by!(Invitation, invitation_id: invitation_id)
|> Repo.preload([:site, :inviter])
user = conn.assigns[:current_user]
existing_membership = Repo.get_by(Membership, user_id: user.id, site_id: invitation.site.id)
multi =
if invitation.role == :owner do
downgrade_previous_owner(Multi.new(), invitation.site)
else
Multi.new()
end
membership_changeset =
Membership.changeset(existing_membership || %Membership{}, %{
user_id: user.id,
site_id: invitation.site.id,
role: invitation.role
})
multi =
multi
|> Multi.insert_or_update(:membership, membership_changeset)
|> Multi.delete(:invitation, invitation)
case Repo.transaction(multi) do
{:ok, _} ->
notify_invitation_accepted(invitation)
Plausible.Billing.SiteLocker.check_sites_for(user)
conn
|> put_flash(:success, "You now have access to #{invitation.site.domain}")
|> redirect(to: "/#{URI.encode_www_form(invitation.site.domain)}")
{:error, _} ->
conn
|> put_flash(:error, "Something went wrong, please try again")
|> redirect(to: "/sites")
end
end
defp downgrade_previous_owner(multi, site) do
prev_owner =
from(
sm in Plausible.Site.Membership,
where: sm.site_id == ^site.id,
where: sm.role == :owner
)
Multi.update_all(multi, :prev_owner, prev_owner, set: [role: :admin])
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)
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)
|> Repo.preload(:site)
Repo.delete!(invitation)
conn
|> put_flash(:success, "You have removed the invitation for #{invitation.email}")
|> redirect(to: Routes.site_path(conn, :settings_general, invitation.site.domain))
end
end