Add admin interface to manage instances subscriptions

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2019-12-03 11:29:51 +01:00
parent 0a96d70348
commit 334d66bf5d
141 changed files with 4198 additions and 1923 deletions

View File

@@ -7,9 +7,9 @@ defmodule Mobilizon.Actors.Actor do
import Ecto.Changeset
alias Mobilizon.{Actors, Config, Crypto}
alias Mobilizon.{Actors, Config, Crypto, Share}
alias Mobilizon.Actors.{ActorOpenness, ActorType, ActorVisibility, Follower, Member}
alias Mobilizon.Events.{Event, FeedToken}
alias Mobilizon.Events.{Event, FeedToken, Comment}
alias Mobilizon.Media.File
alias Mobilizon.Reports.{Note, Report}
alias Mobilizon.Users.User
@@ -43,11 +43,14 @@ defmodule Mobilizon.Actors.Actor do
followers: [Follower.t()],
followings: [Follower.t()],
organized_events: [Event.t()],
comments: [Comment.t()],
feed_tokens: [FeedToken.t()],
created_reports: [Report.t()],
subject_reports: [Report.t()],
report_notes: [Note.t()],
mentions: [Mention.t()],
shares: [Share.t()],
owner_shares: [Share.t()],
memberships: [t]
}
@@ -137,11 +140,14 @@ defmodule Mobilizon.Actors.Actor do
has_many(:followers, Follower, foreign_key: :target_actor_id)
has_many(:followings, Follower, foreign_key: :actor_id)
has_many(:organized_events, Event, foreign_key: :organizer_actor_id)
has_many(:comments, Comment, foreign_key: :actor_id)
has_many(:feed_tokens, FeedToken, foreign_key: :actor_id)
has_many(:created_reports, Report, foreign_key: :reporter_id)
has_many(:subject_reports, Report, foreign_key: :reported_id)
has_many(:report_notes, Note, foreign_key: :moderator_id)
has_many(:mentions, Mention)
has_many(:shares, Share, foreign_key: :actor_id)
has_many(:owner_shares, Share, foreign_key: :owner_actor_id)
many_to_many(:memberships, __MODULE__, join_through: Member)
timestamps()
@@ -217,6 +223,19 @@ defmodule Mobilizon.Actors.Actor do
|> validate_required(@update_required_attrs)
end
@doc false
@spec delete_changeset(t) :: Ecto.Changeset.t()
def delete_changeset(%__MODULE__{} = actor) do
actor
|> change()
|> put_change(:name, nil)
|> put_change(:summary, nil)
|> put_change(:suspended, true)
|> put_change(:avatar, nil)
|> put_change(:banner, nil)
|> put_change(:user_id, nil)
end
@doc """
Changeset for person registration.
"""

View File

@@ -12,6 +12,8 @@ defmodule Mobilizon.Actors do
alias Mobilizon.{Crypto, Events}
alias Mobilizon.Media.File
alias Mobilizon.Storage.{Page, Repo}
alias Mobilizon.Service.Workers.BackgroundWorker
alias Mobilizon.Service.ActivityPub
require Logger
@@ -47,6 +49,7 @@ defmodule Mobilizon.Actors do
@public_visibility [:public, :unlisted]
@administrator_roles [:creator, :administrator]
@actor_preloads [:user, :organized_events, :comments]
@doc """
Gets a single actor.
@@ -224,16 +227,24 @@ defmodule Mobilizon.Actors do
end
end
def delete_actor(%Actor{} = actor) do
BackgroundWorker.enqueue("delete_actor", %{"actor_id" => actor.id})
end
@doc """
Deletes an actor.
"""
@spec delete_actor(Actor.t()) :: {:ok, Actor.t()} | {:error, Ecto.Changeset.t()}
def delete_actor(%Actor{domain: nil} = actor) do
@spec perform(atom(), Actor.t()) :: {:ok, Actor.t()} | {:error, Ecto.Changeset.t()}
def perform(:delete_actor, %Actor{} = actor) do
actor = Repo.preload(actor, @actor_preloads)
transaction =
Multi.new()
|> Multi.delete(:actor, actor)
|> Multi.run(:remove_banner, fn _, %{actor: %Actor{}} -> remove_banner(actor) end)
|> Multi.run(:remove_avatar, fn _, %{actor: %Actor{}} -> remove_avatar(actor) end)
|> Multi.run(:delete_organized_events, fn _, _ -> delete_actor_organized_events(actor) end)
|> Multi.run(:empty_comments, fn _, _ -> delete_actor_empty_comments(actor) end)
|> Multi.run(:remove_banner, fn _, _ -> remove_banner(actor) end)
|> Multi.run(:remove_avatar, fn _, _ -> remove_avatar(actor) end)
|> Multi.update(:actor, Actor.delete_changeset(actor))
|> Repo.transaction()
case transaction do
@@ -245,8 +256,6 @@ defmodule Mobilizon.Actors do
end
end
def delete_actor(%Actor{} = actor), do: Repo.delete(actor)
@doc """
Returns the list of actors.
"""
@@ -486,9 +495,9 @@ defmodule Mobilizon.Actors do
|> Repo.insert()
end
@spec get_or_create_actor_by_url(String.t(), String.t()) ::
@spec get_or_create_instance_actor_by_url(String.t(), String.t()) ::
{:ok, Actor.t()} | {:error, Ecto.Changeset.t()}
def get_or_create_actor_by_url(url, preferred_username \\ "relay") do
def get_or_create_instance_actor_by_url(url, preferred_username \\ "relay") do
case get_actor_by_url(url) do
{:ok, %Actor{} = actor} ->
{:ok, actor}
@@ -571,9 +580,12 @@ defmodule Mobilizon.Actors do
"""
@spec update_follower(Follower.t(), map) :: {:ok, Follower.t()} | {:error, Ecto.Changeset.t()}
def update_follower(%Follower{} = follower, attrs) do
follower
|> Follower.changeset(attrs)
|> Repo.update()
with {:ok, %Follower{} = follower} <-
follower
|> Follower.changeset(attrs)
|> Repo.update() do
{:ok, Repo.preload(follower, [:actor, :target_actor])}
end
end
@doc """
@@ -597,10 +609,10 @@ defmodule Mobilizon.Actors do
Returns the list of followers for an actor.
If actor A and C both follow actor B, actor B's followers are A and C.
"""
@spec list_followers_for_actor(Actor.t()) :: [Follower.t()]
def list_followers_for_actor(%Actor{id: actor_id}) do
@spec list_followers_actors_for_actor(Actor.t()) :: [Actor.t()]
def list_followers_actors_for_actor(%Actor{id: actor_id}) do
actor_id
|> followers_for_actor_query()
|> follower_actors_for_actor_query()
|> Repo.all()
end
@@ -610,18 +622,28 @@ defmodule Mobilizon.Actors do
@spec list_external_followers_for_actor(Actor.t()) :: [Follower.t()]
def list_external_followers_for_actor(%Actor{id: actor_id}) do
actor_id
|> followers_for_actor_query()
|> filter_external()
|> list_external_follower_actors_for_actor_query()
|> Repo.all()
end
@doc """
Returns the paginated list of external followers for an actor.
"""
@spec list_external_followers_for_actor_paginated(Actor.t(), integer | nil, integer | nil) ::
Page.t()
def list_external_followers_for_actor_paginated(%Actor{id: actor_id}, page \\ nil, limit \\ nil) do
actor_id
|> list_external_followers_for_actor_query()
|> Page.build_page(page, limit)
end
@doc """
Build a page struct for followers of an actor.
"""
@spec build_followers_for_actor(Actor.t(), integer | nil, integer | nil) :: Page.t()
def build_followers_for_actor(%Actor{id: actor_id}, page \\ nil, limit \\ nil) do
actor_id
|> followers_for_actor_query()
|> follower_actors_for_actor_query()
|> Page.build_page(page, limit)
end
@@ -632,17 +654,32 @@ defmodule Mobilizon.Actors do
@spec list_followings_for_actor(Actor.t()) :: [Follower.t()]
def list_followings_for_actor(%Actor{id: actor_id}) do
actor_id
|> followings_for_actor_query()
|> followings_actors_for_actor_query()
|> Repo.all()
end
@doc """
Returns the list of external followings for an actor.
"""
@spec list_external_followings_for_actor_paginated(Actor.t(), integer | nil, integer | nil) ::
Page.t()
def list_external_followings_for_actor_paginated(
%Actor{id: actor_id},
page \\ nil,
limit \\ nil
) do
actor_id
|> list_external_followings_for_actor_query()
|> Page.build_page(page, limit)
end
@doc """
Build a page struct for followings of an actor.
"""
@spec build_followings_for_actor(Actor.t(), integer | nil, integer | nil) :: Page.t()
def build_followings_for_actor(%Actor{id: actor_id}, page \\ nil, limit \\ nil) do
actor_id
|> followings_for_actor_query()
|> followings_actors_for_actor_query()
|> Page.build_page(page, limit)
end
@@ -747,7 +784,7 @@ defmodule Mobilizon.Actors do
defp actor_with_preload_query(actor_id) do
from(
a in Actor,
where: a.id == ^actor_id,
where: a.id == ^actor_id and not a.suspended,
preload: [:organized_events, :followers, :followings]
)
end
@@ -885,12 +922,13 @@ defmodule Mobilizon.Actors do
defp follower_by_followed_and_following_query(followed_id, follower_id) do
from(
f in Follower,
where: f.target_actor_id == ^followed_id and f.actor_id == ^follower_id
where: f.target_actor_id == ^followed_id and f.actor_id == ^follower_id,
preload: [:actor, :target_actor]
)
end
@spec followers_for_actor_query(integer | String.t()) :: Ecto.Query.t()
defp followers_for_actor_query(actor_id) do
@spec follower_actors_for_actor_query(integer | String.t()) :: Ecto.Query.t()
defp follower_actors_for_actor_query(actor_id) do
from(
a in Actor,
join: f in Follower,
@@ -899,8 +937,18 @@ defmodule Mobilizon.Actors do
)
end
@spec followings_for_actor_query(integer | String.t()) :: Ecto.Query.t()
defp followings_for_actor_query(actor_id) do
@spec follower_for_actor_query(integer | String.t()) :: Ecto.Query.t()
defp follower_for_actor_query(actor_id) do
from(
f in Follower,
join: a in Actor,
on: a.id == f.actor_id,
where: f.target_actor_id == ^actor_id
)
end
@spec followings_actors_for_actor_query(integer | String.t()) :: Ecto.Query.t()
defp followings_actors_for_actor_query(actor_id) do
from(
a in Actor,
join: f in Follower,
@@ -909,6 +957,38 @@ defmodule Mobilizon.Actors do
)
end
@spec followings_for_actor_query(integer | String.t()) :: Ecto.Query.t()
defp followings_for_actor_query(actor_id) do
from(
f in Follower,
join: a in Actor,
on: a.id == f.target_actor_id,
where: f.actor_id == ^actor_id
)
end
@spec list_external_follower_actors_for_actor_query(integer) :: Ecto.Query.t()
defp list_external_follower_actors_for_actor_query(actor_id) do
actor_id
|> follower_actors_for_actor_query()
|> filter_external()
end
@spec list_external_followers_for_actor_query(integer) :: Ecto.Query.t()
defp list_external_followers_for_actor_query(actor_id) do
actor_id
|> follower_for_actor_query()
|> filter_follower_actors_external()
end
@spec list_external_followings_for_actor_query(integer) :: Ecto.Query.t()
defp list_external_followings_for_actor_query(actor_id) do
actor_id
|> followings_for_actor_query()
|> filter_follower_actors_external()
|> order_by(desc: :updated_at)
end
@spec filter_local(Ecto.Query.t()) :: Ecto.Query.t()
defp filter_local(query) do
from(a in query, where: is_nil(a.domain))
@@ -919,8 +999,16 @@ defmodule Mobilizon.Actors do
from(a in query, where: not is_nil(a.domain))
end
@spec filter_follower_actors_external(Ecto.Query.t()) :: Ecto.Query.t()
defp filter_follower_actors_external(query) do
query
|> where([_f, a], not is_nil(a.domain))
|> preload([f, a], [:target_actor, :actor])
end
@spec filter_by_type(Ecto.Query.t(), ActorType.t()) :: Ecto.Query.t()
defp filter_by_type(query, type) when type in [:Person, :Group] do
defp filter_by_type(query, type)
when type in [:Person, :Group, :Application, :Service, :Organisation] do
from(a in query, where: a.type == ^type)
end
@@ -943,4 +1031,36 @@ defmodule Mobilizon.Actors do
@spec preload_followers(Actor.t(), boolean) :: Actor.t()
defp preload_followers(actor, true), do: Repo.preload(actor, [:followers])
defp preload_followers(actor, false), do: actor
defp delete_actor_organized_events(%Actor{organized_events: organized_events}) do
res =
Enum.map(organized_events, fn event ->
event =
Repo.preload(event, [:organizer_actor, :participants, :picture, :mentions, :comments])
ActivityPub.delete(event, false)
end)
if Enum.all?(res, fn {status, _, _} -> status == :ok end) do
{:ok, res}
else
{:error, res}
end
end
defp delete_actor_empty_comments(%Actor{comments: comments}) do
res =
Enum.map(comments, fn comment ->
comment =
Repo.preload(comment, [:actor, :mentions, :event, :in_reply_to_comment, :origin_comment])
ActivityPub.delete(comment, false)
end)
if Enum.all?(res, fn {status, _, _} -> status == :ok end) do
{:ok, res}
else
{:error, res}
end
end
end

View File

@@ -19,11 +19,15 @@ defmodule Mobilizon.Actors.Follower do
@required_attrs [:url, :approved, :target_actor_id, :actor_id]
@attrs @required_attrs
@timestamps_opts [type: :utc_datetime]
@primary_key {:id, :binary_id, autogenerate: true}
schema "followers" do
field(:approved, :boolean, default: false)
field(:url, :string)
timestamps()
belongs_to(:target_actor, Actor)
belongs_to(:actor, Actor)
end

View File

@@ -32,7 +32,6 @@ defmodule Mobilizon.Events.Comment do
# When deleting an event we only nihilify everything
@required_attrs [:url]
@creation_required_attrs @required_attrs ++ [:text, :actor_id]
@deletion_required_attrs @required_attrs ++ [:deleted_at]
@optional_attrs [
:text,
:actor_id,
@@ -81,11 +80,13 @@ defmodule Mobilizon.Events.Comment do
|> validate_required(@creation_required_attrs)
end
@spec delete_changeset(t, map) :: Ecto.Changeset.t()
def delete_changeset(%__MODULE__{} = comment, attrs) do
@spec delete_changeset(t) :: Ecto.Changeset.t()
def delete_changeset(%__MODULE__{} = comment) do
comment
|> common_changeset(attrs)
|> validate_required(@deletion_required_attrs)
|> change()
|> put_change(:text, nil)
|> put_change(:actor_id, nil)
|> put_change(:deleted_at, DateTime.utc_now() |> DateTime.truncate(:second))
end
@doc """

View File

@@ -13,6 +13,8 @@ defmodule Mobilizon.Events.Event do
alias Mobilizon.Addresses
alias Mobilizon.Events
alias Mobilizon.Events.{
Comment,
EventOptions,
@@ -73,6 +75,7 @@ defmodule Mobilizon.Events.Event do
:category,
:status,
:draft,
:local,
:visibility,
:join_options,
:publish_at,
@@ -190,13 +193,16 @@ defmodule Mobilizon.Events.Event do
def can_be_managed_by(_event, _actor), do: {:event_can_be_managed, false}
@spec put_tags(Changeset.t(), map) :: Changeset.t()
defp put_tags(%Changeset{} = changeset, %{tags: tags}),
do: put_assoc(changeset, :tags, Enum.map(tags, &process_tag/1))
defp put_tags(%Changeset{} = changeset, %{tags: tags}) do
put_assoc(changeset, :tags, Enum.map(tags, &process_tag/1))
end
defp put_tags(%Changeset{} = changeset, _), do: changeset
# We need a changeset instead of a raw struct because of slug which is generated in changeset
defp process_tag(%{id: _id} = tag), do: tag
defp process_tag(%{id: id} = _tag) do
Events.get_tag(id)
end
defp process_tag(tag) do
Tag.changeset(%Tag{}, tag)

View File

@@ -39,6 +39,21 @@ defmodule Mobilizon.Events.EventParticipantStats do
@doc false
@spec changeset(t, map) :: Ecto.Changeset.t()
def changeset(%__MODULE__{} = event_options, attrs) do
cast(event_options, attrs, @attrs)
event_options
|> cast(attrs, @attrs)
|> validate_stats()
end
defp validate_stats(%Ecto.Changeset{} = changeset) do
changeset
|> validate_number(:not_approved, greater_than_or_equal_to: 0)
|> validate_number(:rejected, greater_than_or_equal_to: 0)
|> validate_number(:participant, greater_than_or_equal_to: 0)
|> validate_number(:moderator, greater_than_or_equal_to: 0)
|> validate_number(:administrator, greater_than_or_equal_to: 0)
|> validate_number(:creator, greater_than_or_equal_to: 0)
# TODO: Replace me with something like the following
# Enum.reduce(@attrs, fn key, changeset -> validate_number(changeset, key, greater_than_or_equal_to: 0) end)
end
end

View File

@@ -97,6 +97,7 @@ defmodule Mobilizon.Events do
@comment_preloads [
:actor,
:event,
:attributed_to,
:in_reply_to_comment,
:origin_comment,
@@ -722,6 +723,13 @@ defmodule Mobilizon.Events do
|> Repo.all()
end
@spec list_actors_participants_for_event(String.t()) :: [Actor.t()]
def list_actors_participants_for_event(id) do
id
|> list_participant_actors_for_event_query
|> Repo.all()
end
@doc """
Returns the list of participations for an actor.
@@ -864,30 +872,15 @@ defmodule Mobilizon.Events do
|> Multi.run(:update_event_participation_stats, fn _repo,
%{
participant:
%Participant{
role: role,
event_id: event_id
} = _participant
%Participant{role: new_role} =
participant
} ->
with {:update_event_participation_stats, true} <-
{:update_event_participation_stats, update_event_participation_stats},
{:ok, %Event{} = event} <- get_event(event_id),
%EventParticipantStats{} = participant_stats <-
Map.get(event, :participant_stats),
%EventParticipantStats{} = participant_stats <-
Map.update(participant_stats, role, 0, &(&1 + 1)),
{:ok, %Event{} = event} <-
event
|> Event.update_changeset(%{
participant_stats: Map.from_struct(participant_stats)
})
|> Repo.update() do
{:ok, event}
else
{:update_event_participation_stats, false} -> {:ok, nil}
{:error, :event_not_found} -> {:error, :event_not_found}
err -> {:error, err}
end
update_participant_stats(
participant,
nil,
new_role,
update_event_participation_stats
)
end)
|> Repo.transaction() do
{:ok, Repo.preload(participant, [:event, :actor])}
@@ -899,10 +892,21 @@ defmodule Mobilizon.Events do
"""
@spec update_participant(Participant.t(), map) ::
{:ok, Participant.t()} | {:error, Changeset.t()}
def update_participant(%Participant{} = participant, attrs) do
participant
|> Participant.changeset(attrs)
|> Repo.update()
def update_participant(%Participant{role: old_role} = participant, attrs) do
with {:ok, %{participant: %Participant{} = participant}} <-
Multi.new()
|> Multi.update(:participant, Participant.changeset(participant, attrs))
|> Multi.run(:update_event_participation_stats, fn _repo,
%{
participant:
%Participant{role: new_role} =
participant
} ->
update_participant_stats(participant, old_role, new_role)
end)
|> Repo.transaction() do
{:ok, Repo.preload(participant, [:event, :actor])}
end
end
@doc """
@@ -910,7 +914,71 @@ defmodule Mobilizon.Events do
"""
@spec delete_participant(Participant.t()) ::
{:ok, Participant.t()} | {:error, Changeset.t()}
def delete_participant(%Participant{} = participant), do: Repo.delete(participant)
def delete_participant(%Participant{role: old_role} = participant) do
with {:ok, %{participant: %Participant{} = participant}} <-
Multi.new()
|> Multi.delete(:participant, participant)
|> Multi.run(:update_event_participation_stats, fn _repo,
%{
participant:
%Participant{} = participant
} ->
update_participant_stats(participant, old_role, nil)
end)
|> Repo.transaction() do
{:ok, participant}
end
end
defp update_participant_stats(
%Participant{
event_id: event_id
} = _participant,
old_role,
new_role,
update_event_participation_stats \\ true
) do
with {:update_event_participation_stats, true} <-
{:update_event_participation_stats, update_event_participation_stats},
{:ok, %Event{} = event} <- get_event(event_id),
%EventParticipantStats{} = participant_stats <-
Map.get(event, :participant_stats),
%EventParticipantStats{} = participant_stats <-
do_update_participant_stats(participant_stats, old_role, new_role),
{:ok, %Event{} = event} <-
event
|> Event.update_changeset(%{
participant_stats: Map.from_struct(participant_stats)
})
|> Repo.update() do
{:ok, event}
else
{:update_event_participation_stats, false} ->
{:ok, nil}
{:error, :event_not_found} ->
{:error, :event_not_found}
err ->
{:error, err}
end
end
defp do_update_participant_stats(participant_stats, old_role, new_role) do
participant_stats
|> decrease_participant_stats(old_role)
|> increase_participant_stats(new_role)
end
defp increase_participant_stats(participant_stats, nil), do: participant_stats
defp increase_participant_stats(participant_stats, role),
do: Map.update(participant_stats, role, 0, &(&1 + 1))
defp decrease_participant_stats(participant_stats, nil), do: participant_stats
defp decrease_participant_stats(participant_stats, role),
do: Map.update(participant_stats, role, 0, &(&1 - 1))
@doc """
Gets a single session.
@@ -1170,11 +1238,7 @@ defmodule Mobilizon.Events do
@spec delete_comment(Comment.t()) :: {:ok, Comment.t()} | {:error, Changeset.t()}
def delete_comment(%Comment{} = comment) do
comment
|> Comment.delete_changeset(%{
text: nil,
actor_id: nil,
deleted_at: DateTime.utc_now()
})
|> Comment.delete_changeset()
|> Repo.update()
end
@@ -1561,14 +1625,22 @@ defmodule Mobilizon.Events do
defp list_participants_for_event_query(event_id) do
from(
p in Participant,
join: e in Event,
on: p.event_id == e.id,
where: e.id == ^event_id,
where: p.event_id == ^event_id,
preload: [:actor]
)
end
@spec list_participants_for_event_query(String.t()) :: Ecto.Query.t()
@spec list_participant_actors_for_event_query(String.t()) :: Ecto.Query.t()
defp list_participant_actors_for_event_query(event_id) do
from(
a in Actor,
join: p in Participant,
on: p.actor_id == a.id,
where: p.event_id == ^event_id
)
end
@spec list_local_emails_user_participants_for_event_query(String.t()) :: Ecto.Query.t()
def list_local_emails_user_participants_for_event_query(event_id) do
Participant
|> join(:inner, [p], a in Actor, on: p.actor_id == a.id and is_nil(a.domain))

View File

@@ -57,6 +57,7 @@ defmodule Mobilizon.Events.Participant do
|> cast(attrs, @attrs)
|> ensure_url()
|> validate_required(@required_attrs)
|> unique_constraint(:actor_id, name: :participants_event_id_actor_id_index)
end
# If there's a blank URL that's because we're doing the first insert

75
lib/mobilizon/share.ex Normal file
View File

@@ -0,0 +1,75 @@
defmodule Mobilizon.Share do
@moduledoc """
Holds the list of shares made to external actors
"""
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query
alias Mobilizon.Storage.Repo
alias Mobilizon.Actors.Actor
@type t :: %__MODULE__{
uri: String.t(),
actor: Actor.t()
}
@required_attrs [:uri, :actor_id, :owner_actor_id]
@optional_attrs []
@attrs @required_attrs ++ @optional_attrs
schema "shares" do
field(:uri, :string)
belongs_to(:actor, Actor)
belongs_to(:owner_actor, Actor)
timestamps()
end
@doc false
def changeset(share, attrs) do
share
|> cast(attrs, @attrs)
|> validate_required(@required_attrs)
|> foreign_key_constraint(:actor_id)
|> unique_constraint(:uri, name: :shares_uri_actor_id_index)
end
@spec create(String.t(), integer(), integer()) ::
{:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}
def create(uri, actor_id, owner_actor_id) do
%__MODULE__{}
|> changeset(%{actor_id: actor_id, owner_actor_id: owner_actor_id, uri: uri})
|> Repo.insert(on_conflict: :nothing)
end
@spec get(String.t(), integer()) :: Ecto.Schema.t() | nil
def get(uri, actor_id) do
__MODULE__
|> where(actor_id: ^actor_id, uri: ^uri)
|> Repo.one()
end
@spec get_actors_by_share_uri(String.t()) :: [Ecto.Schema.t()]
def get_actors_by_share_uri(uri) do
Actor
|> join(:inner, [a], s in __MODULE__, on: s.actor_id == a.id)
|> where([_a, s], s.uri == ^uri)
|> Repo.all()
end
@spec get_actors_by_owner_actor_id(integer()) :: [Ecto.Schema.t()]
def get_actors_by_owner_actor_id(actor_id) do
Actor
|> join(:inner, [a], s in __MODULE__, on: s.actor_id == a.id)
|> where([_a, s], s.owner_actor_id == ^actor_id)
|> Repo.all()
end
@spec delete_all_by_uri(String.t()) :: {integer(), nil | [term()]}
def delete_all_by_uri(uri) do
__MODULE__
|> where(uri: ^uri)
|> Repo.delete_all()
end
end

View File

@@ -28,7 +28,7 @@ defmodule Mobilizon.Tombstone do
def changeset(%__MODULE__{} = tombstone, attrs) do
tombstone
|> cast(attrs, @attrs)
|> validate_required(@attrs)
|> validate_required(@required_attrs)
end
@spec create_tombstone(map()) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}