analytics/lib/plausible/teams/users.ex

199 lines
4.6 KiB
Elixir

defmodule Plausible.Teams.Users do
@moduledoc """
Users API accounting for teams.
"""
import Ecto.Query
alias Plausible.Repo
alias Plausible.Teams
def owned_teams(user) do
Repo.all(teams_query(user, roles: :owner))
end
def teams(user, opts \\ []) do
user
|> teams_query(Keyword.merge([order_by: :name], opts))
|> Repo.all()
|> Repo.preload(:owners)
end
def teams_query(user, opts \\ []) do
order_by = Keyword.get(opts, :order_by, :name)
roles = Keyword.get(opts, :roles)
identifier = Keyword.get(opts, :identifier)
query =
from(
tm in Teams.Membership,
as: :team_membership,
inner_join: t in assoc(tm, :team),
as: :team,
where: tm.user_id == ^user.id,
where: tm.role != :guest,
select: t
)
query =
if roles do
where(query, [team_membership: tm], tm.role in ^List.wrap(roles))
else
query
end
query =
if identifier do
where(query, [team: team], team.identifier == ^identifier)
else
query
end
case order_by do
:name ->
order_by(query, [team: t], [t.name, t.id])
:id_desc ->
order_by(query, [team: t], desc: t.id)
_ ->
query
end
end
def team_member?(user, opts \\ []) do
excluded_team_ids = Keyword.get(opts, :except, [])
only_setup? = Keyword.get(opts, :only_setup?, false)
query =
from(
tm in Teams.Membership,
where: tm.user_id == ^user.id,
where: tm.role != :guest,
where: tm.team_id not in ^excluded_team_ids
)
query =
if only_setup? do
query
|> join(:inner, [tm], t in assoc(tm, :team), as: :team)
|> where([team: t], t.setup_complete == true)
else
query
end
Repo.exists?(query)
end
def has_sites?(user, opts \\ []) do
include_pending? = Keyword.get(opts, :include_pending?, false)
sites_query =
from(
s in Plausible.Site,
where: s.team_id == parent_as(:site_parent).team_id,
select: 1
)
# NOTE: Provided guest team memberships are consistently pruned,
# mere presence of guest team membership means there are sites
# associated with it, so querying for team membership alone
# should be enough.
team_member_query =
from(
tm in Teams.Membership,
as: :site_parent,
where: exists(sites_query),
where: tm.user_id == ^user.id,
select: 1
)
query =
if include_pending? do
site_transfer_query =
from(
st in Teams.SiteTransfer,
where: st.email == ^user.email,
select: 1
)
# NOTE: The same principle applies to guest team invitations,
# as it's pruned as well when there are no more guest invitations
# associated with it.
member_invitation_query =
from(
ti in Teams.Invitation,
as: :site_parent,
where: exists(sites_query),
where: ti.email == ^user.email,
select: 1
)
team_member_query
|> union_all(^site_transfer_query)
|> union_all(^member_invitation_query)
else
team_member_query
end
Repo.exists?(query)
end
def owns_sites?(user, opts \\ []) do
include_pending? = Keyword.get(opts, :include_pending?, false)
only_team = Keyword.get(opts, :only_team)
sites_query =
from(
s in Plausible.Site,
where: s.team_id == parent_as(:site_parent).team_id,
select: 1
)
owner_query =
from(
tm in Teams.Membership,
as: :site_parent,
where: exists(sites_query),
where: tm.user_id == ^user.id,
where: tm.role == :owner,
select: 1
)
owner_query =
if only_team do
where(owner_query, [tm], tm.team_id == ^only_team.id)
else
owner_query
end
query =
if include_pending? do
site_transfer_query =
from(
st in Teams.SiteTransfer,
where: st.email == ^user.email,
select: 1
)
owner_invitation_query =
from(
ti in Teams.Invitation,
as: :site_parent,
where: exists(sites_query),
where: ti.email == ^user.email,
where: ti.role == :owner,
select: 1
)
owner_query
|> union_all(^site_transfer_query)
|> union_all(^owner_invitation_query)
else
owner_query
end
Repo.exists?(query)
end
end