Skip to content

Commit

Permalink
Make deleting projects soft, and rename to archival (closes #731)
Browse files Browse the repository at this point in the history
  • Loading branch information
milesmcc committed Dec 3, 2023
1 parent 9fec557 commit 5660bc7
Show file tree
Hide file tree
Showing 10 changed files with 255 additions and 168 deletions.
125 changes: 57 additions & 68 deletions platform/lib/platform/permissions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ defmodule Platform.Permissions do
can_create_project?(user)
end

def can_edit_project_metadata?(%User{} = _, %Project{active: false}) do
false
end

def can_edit_project_metadata?(%User{} = user, %Project{} = project) do
case Projects.get_project_membership_by_user_and_project(user, project) do
%Projects.ProjectMembership{role: :owner} -> true
Expand Down Expand Up @@ -68,14 +72,30 @@ defmodule Platform.Permissions do
end
end

defmemo _is_media_editable?(%Media{project_id: nil} = media), expires_in: 5000 do
true
end

defmemo _is_media_editable?(%Media{} = media), expires_in: 5000 do
# Security mode must be normal, the media can't be deleted, and its project must be active.

with :normal <- Platform.Security.get_security_mode_state(),
false <- media.deleted,
true <- Projects.get_project(media.project_id).active do
true
else
_ -> false
end
end

def can_api_token_post_comment?(%APIToken{} = token, %Media{} = media) do
Enum.member?(token.permissions, :comment) and token.is_active and
token.project_id == media.project_id
token.project_id == media.project_id and _is_media_editable?(media)
end

def can_api_token_edit_media?(%APIToken{} = token, %Media{} = media) do
Enum.member?(token.permissions, :edit) and token.is_active and
token.project_id == media.project_id
token.project_id == media.project_id and _is_media_editable?(media)
end

def can_api_token_update_attribute?(
Expand All @@ -86,13 +106,17 @@ defmodule Platform.Permissions do
can_api_token_edit_media?(token, media)
end

def can_delete_project?(%User{} = user, %Project{} = project) do
def can_change_project_active_status?(%User{} = user, %Project{} = project) do
case Projects.get_project_membership_by_user_and_project(user, project) do
%Projects.ProjectMembership{role: :owner} -> true
_ -> false
end
end

def can_add_media_to_project?(%User{} = user, %Project{active: false} = project) do
false
end

def can_add_media_to_project?(%User{} = user, %Project{} = project) do
case Projects.get_project_membership_by_user_and_project(user, project) do
%Projects.ProjectMembership{role: :owner} -> true
Expand All @@ -102,6 +126,10 @@ defmodule Platform.Permissions do
end
end

def can_bulk_upload_media_to_project?(%User{} = user, %Project{active: false} = project) do
false
end

def can_bulk_upload_media_to_project?(%User{} = user, %Project{} = project) do
case Projects.get_project_membership_by_user_and_project(user, project) do
%Projects.ProjectMembership{role: :owner} -> true
Expand All @@ -111,6 +139,7 @@ defmodule Platform.Permissions do
end

def can_delete_media?(%User{} = user, %Media{} = media) do
# This is a soft delete
case Projects.get_project_membership_by_user_and_project_id(user, media.project_id) do
%Projects.ProjectMembership{role: :owner} -> true
%Projects.ProjectMembership{role: :manager} -> true
Expand Down Expand Up @@ -152,46 +181,21 @@ defmodule Platform.Permissions do

def can_edit_media?(%User{} = user, %Media{} = media) do
# This includes uploading new media versions as well as editing attributes.

membership = Projects.get_project_membership_by_user_and_project_id(user, media.project_id)

# This logic would be nice to refactor into a `with` statement
case Platform.Security.get_security_mode_state() do
:normal ->
case Enum.member?(user.restrictions || [], :muted) do
true ->
false

false ->
cond do
is_nil(media.slug) ->
# This is a new media object that hasn't been saved yet.
true

is_nil(media.project) ->
# This media object is not associated with a project.
true

is_nil(membership) ->
false

membership.role == :owner ->
true

membership.role == :manager ->
true

membership.role == :editor ->
with true <- _is_media_editable?(media),
true <- can_view_media?(user, media),
false <- is_nil(membership),
false <- Enum.member?(user.restrictions || [], :muted),
true <-
is_nil(media.slug) or is_nil(media.project) or
membership.role == :owner or membership.role == :manager or
(membership.role == :editor and
not (Enum.member?(media.attr_restrictions || [], "Frozen") ||
media.attr_status == "Completed" || media.attr_status == "Cancelled")

true ->
false
end
end

_ ->
Accounts.is_admin(user)
media.attr_status == "Completed" || media.attr_status == "Cancelled")) do
true
else
_ -> false
end
end

Expand Down Expand Up @@ -230,34 +234,19 @@ defmodule Platform.Permissions do
if Accounts.is_auto_account(user) do
true
else
# This logic would be nice to refactor into a `with` statement
case Platform.Security.get_security_mode_state() do
:normal ->
case Enum.member?(user.restrictions || [], :muted) do
true ->
false

false ->
case media.attr_restrictions do
nil ->
can_view_media?(user, media)

values ->
# Restrictions are present.
if not is_nil(media.project) and
(Enum.member?(values, "Hidden") || Enum.member?(values, "Frozen")) do
membership =
Projects.get_project_membership_by_user_and_project(user, media.project)

membership.role == :owner or membership.role == :manager
else
true
end
end
end

_ ->
false
membership = Projects.get_project_membership_by_user_and_project_id(user, media.project_id)

with true <- _is_media_editable?(media),
true <- can_view_media?(user, media),
true <-
is_nil(media.attr_restrictions) or
(not is_nil(media.project) and
(Enum.member?(media.attr_restrictions, "Hidden") or
Enum.member?(media.attr_restrictions, "Frozen")) and
(membership.role == :owner or membership.role == :manager)) do
true
else
_ -> false
end
end
end
Expand Down
21 changes: 9 additions & 12 deletions platform/lib/platform/projects.ex
Original file line number Diff line number Diff line change
Expand Up @@ -147,19 +147,16 @@ defmodule Platform.Projects do
end

@doc """
Deletes a project.
## Examples
iex> delete_project(project)
{:ok, %Project{}}
iex> delete_project(project)
{:error, %Ecto.Changeset{}}
Marks a project as inactive.
"""
def delete_project(%Project{} = project) do
Repo.delete(project)
def update_project_active(%Project{} = project, is_active, user \\ nil) do
unless is_nil(user) or Permissions.can_change_project_active_status?(user, project) do
raise "User does not have permission to change the active status of this project"
end

project
|> Project.active_changeset(%{active: is_active})
|> Repo.update()
end

@doc """
Expand Down
8 changes: 8 additions & 0 deletions platform/lib/platform/projects/project.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ defmodule Platform.Projects.Project do
field(:name, :string)
field(:description, :string, default: "")
field(:color, :string, default: "#fb923c")
field(:active, :boolean, default: true)

embeds_many(:attributes, Platform.Projects.ProjectAttribute, on_replace: :delete)

Expand Down Expand Up @@ -37,6 +38,13 @@ defmodule Platform.Projects.Project do
|> validate_length(:description, min: 0, max: 1000)
|> validate_format(:color, ~r/^#[0-9a-fA-F]{6}$/)
end

@doc false
def active_changeset(project, attrs) do
project
|> changeset(attrs)
|> cast(attrs, [:active])
end
end

defimpl Jason.Encoder, for: Platform.Projects.Project do
Expand Down
Loading

0 comments on commit 5660bc7

Please sign in to comment.