Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 43 additions & 8 deletions lib/textbin/organizations.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ defmodule Textbin.Organizations do
alias Textbin.Accounts.{Scope, User}
alias Textbin.Organizations.{Organization, OrganizationMembership, Policy}
alias Textbin.Organizations.{Workspace, WorkspaceMembership}
alias Textbin.Pastes.Paste
alias Textbin.Repo

def create_organization(%Scope{user: %User{} = creator}, attrs) do
Expand Down Expand Up @@ -282,14 +283,21 @@ defmodule Textbin.Organizations do
def join_workspace(_, _), do: {:error, :not_found}

def change_workspace_visibility(%Scope{} = scope, %Workspace{} = workspace, visibility) do
change_workspace_settings(scope, workspace, %{visibility: visibility})
end

def change_workspace_visibility(_, _, _), do: {:error, :not_found}

def change_workspace_settings(%Scope{} = scope, %Workspace{} = workspace, attrs)
when is_map(attrs) do
workspace_transaction(
scope,
workspace,
&change_workspace_visibility_in_transaction(&1, &2, &3, &4, visibility)
&change_workspace_settings_in_transaction(&1, &2, &3, &4, attrs)
)
end

def change_workspace_visibility(_, _, _), do: {:error, :not_found}
def change_workspace_settings(_, _, _), do: {:error, :not_found}

def delete_workspace(%Scope{} = scope, %Workspace{} = workspace) do
workspace_transaction(scope, workspace, &delete_workspace_in_transaction/4)
Expand Down Expand Up @@ -482,20 +490,47 @@ defmodule Textbin.Organizations do
end
end

defp change_workspace_visibility_in_transaction(
defp change_workspace_settings_in_transaction(
_organization,
_org_actor,
actor,
workspace,
visibility
attrs
) do
with :ok <- Policy.authorize_workspace_change(actor) do
workspace
|> Workspace.changeset(%{visibility: visibility})
|> Repo.update()
with :ok <- Policy.authorize_workspace_change(actor),
{:ok, updated_workspace} <-
workspace
|> Workspace.changeset(attrs)
|> Repo.update() do
clamp_paste_audiences(updated_workspace)
{:ok, updated_workspace}
end
end

defp clamp_paste_audiences(%Workspace{id: workspace_id, external_sharing_policy: "disabled"}) do
updated_at = Paste.utc_now_ms()

Repo.update_all(
from(paste in Paste,
where: paste.workspace_id == ^workspace_id and paste.audience != "workspace"
),
set: [audience: "workspace", updated_at: updated_at]
)
end

defp clamp_paste_audiences(%Workspace{id: workspace_id, external_sharing_policy: "unlisted"}) do
updated_at = Paste.utc_now_ms()

Repo.update_all(
from(paste in Paste,
where: paste.workspace_id == ^workspace_id and paste.audience == "public"
),
set: [audience: "unlisted", updated_at: updated_at]
)
end

defp clamp_paste_audiences(%Workspace{external_sharing_policy: "public"}), do: {0, nil}

defp delete_workspace_in_transaction(_organization, _org_actor, actor, workspace) do
with :ok <- Policy.authorize_workspace_change(actor),
false <- workspace.is_default do
Expand Down
31 changes: 29 additions & 2 deletions lib/textbin/organizations/workspace.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ defmodule Textbin.Organizations.Workspace do
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
@visibilities ["open", "private"]
@external_sharing_policies ["disabled", "unlisted", "public"]

schema "workspaces" do
field :name, :string
field :slug, :string
field :visibility, :string, default: "open"
field :external_sharing_policy, :string, default: "disabled"
field :is_default, :boolean, default: false

belongs_to :organization, Textbin.Organizations.Organization
Expand All @@ -23,19 +25,44 @@ defmodule Textbin.Organizations.Workspace do

def changeset(workspace, attrs) do
workspace
|> cast(attrs, [:name, :slug, :visibility])
|> validate_required([:organization_id, :name, :slug, :visibility, :is_default])
|> cast(attrs, [:name, :slug, :visibility, :external_sharing_policy])
|> put_default_external_sharing_policy(attrs)
|> validate_required([
:organization_id,
:name,
:slug,
:visibility,
:external_sharing_policy,
:is_default
])
|> validate_length(:name, max: 160)
|> validate_length(:slug, max: 100)
|> validate_format(:slug, ~r/^[a-z0-9]+(?:-[a-z0-9]+)*$/)
|> validate_inclusion(:visibility, @visibilities)
|> validate_inclusion(:external_sharing_policy, @external_sharing_policies)
|> validate_default_visibility()
|> unique_constraint([:organization_id, :slug])
|> unique_constraint(:organization_id, name: :workspaces_one_default_per_organization)
|> foreign_key_constraint(:organization_id)
|> foreign_key_constraint(:created_by_id)
end

defp put_default_external_sharing_policy(changeset, attrs) do
if policy_provided?(attrs) or changeset.data.id do
changeset
else
policy = if get_field(changeset, :visibility) == "private", do: "disabled", else: "public"
put_change(changeset, :external_sharing_policy, policy)
end
end

defp policy_provided?(attrs) when is_map(attrs) do
Map.has_key?(attrs, "external_sharing_policy") or
Map.has_key?(attrs, :external_sharing_policy)
end

defp policy_provided?(_attrs), do: false

defp validate_default_visibility(changeset) do
if get_field(changeset, :is_default) && get_field(changeset, :visibility) != "open" do
add_error(changeset, :visibility, "must be open for the default workspace")
Expand Down
Loading
Loading