Add visibility to actors

Also use url helpers to generate urls properly

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2019-04-25 19:05:05 +02:00
parent 7cd4df0ce9
commit 12116ba6fa
19 changed files with 392 additions and 234 deletions

View File

@@ -14,6 +14,14 @@ defenum(Mobilizon.Actors.ActorOpennessEnum, :actor_openness, [
:open
])
defenum(Mobilizon.Actors.ActorVisibilityEnum, :actor_visibility_type, [
:public,
:unlisted,
# Probably unused
:restricted,
:private
])
defmodule Mobilizon.Actors.Actor do
@moduledoc """
Represents an actor (local and remote actors)
@@ -26,6 +34,9 @@ defmodule Mobilizon.Actors.Actor do
alias Mobilizon.Actors.{Actor, Follower, Member}
alias Mobilizon.Events.{Event, FeedToken}
alias MobilizonWeb.Router.Helpers, as: Routes
alias MobilizonWeb.Endpoint
import Ecto.Query
import Mobilizon.Ecto
alias Mobilizon.Repo
@@ -49,6 +60,7 @@ defmodule Mobilizon.Actors.Actor do
field(:keys, :string)
field(:manually_approves_followers, :boolean, default: false)
field(:openness, Mobilizon.Actors.ActorOpennessEnum, default: :moderated)
field(:visibility, Mobilizon.Actors.ActorVisibilityEnum, default: :private)
field(:suspended, :boolean, default: false)
field(:avatar_url, :string)
field(:banner_url, :string)
@@ -217,24 +229,43 @@ defmodule Mobilizon.Actors.Actor do
@spec build_urls(Ecto.Changeset.t(), atom()) :: Ecto.Changeset.t()
defp build_urls(changeset, type \\ :Person)
defp build_urls(%Ecto.Changeset{changes: %{preferred_username: username}} = changeset, type) do
symbol = if type == :Group, do: "~", else: "@"
defp build_urls(%Ecto.Changeset{changes: %{preferred_username: username}} = changeset, _type) do
changeset
|> put_change(
:outbox_url,
"#{MobilizonWeb.Endpoint.url()}/#{symbol}#{username}/outbox"
build_url(username, :outbox)
)
|> put_change(
:inbox_url,
"#{MobilizonWeb.Endpoint.url()}/#{symbol}#{username}/inbox"
build_url(username, :inbox)
)
|> put_change(:shared_inbox_url, "#{MobilizonWeb.Endpoint.url()}/inbox")
|> put_change(:url, "#{MobilizonWeb.Endpoint.url()}/#{symbol}#{username}")
|> put_change(:url, build_url(username, :page))
end
defp build_urls(%Ecto.Changeset{} = changeset, _type), do: changeset
@doc """
Build an AP URL for an actor
"""
@spec build_url(String.t(), atom()) :: String.t()
def build_url(preferred_username, endpoint, args \\ [])
def build_url(preferred_username, :page, args) do
Endpoint
|> Routes.page_url(:actor, preferred_username, args)
|> URI.decode()
end
def build_url(username, :inbox, _args), do: "#{build_url(username, :page)}/inbox"
def build_url(preferred_username, endpoint, args)
when endpoint in [:outbox, :following, :followers] do
Endpoint
|> Routes.activity_pub_url(endpoint, preferred_username, args)
|> URI.decode()
end
@doc """
Get a public key for a given ActivityPub actor ID (url)
"""
@@ -272,8 +303,24 @@ defmodule Mobilizon.Actors.Actor do
If actor A and C both follow actor B, actor B's followers are A and C
"""
@spec get_followers(struct(), number(), number()) :: list()
@spec get_followers(struct(), number(), number()) :: map()
def get_followers(%Actor{id: actor_id} = _actor, page \\ nil, limit \\ nil) do
query =
from(
a in Actor,
join: f in Follower,
on: a.id == f.actor_id,
where: f.target_actor_id == ^actor_id
)
total = Task.async(fn -> Repo.aggregate(query, :count, :id) end)
elements = Task.async(fn -> Repo.all(paginate(query, page, limit)) end)
%{total: Task.await(total), elements: Task.await(elements)}
end
@spec get_full_followers(struct()) :: list()
def get_full_followers(%Actor{id: actor_id} = _actor) do
Repo.all(
from(
a in Actor,
@@ -281,7 +328,6 @@ defmodule Mobilizon.Actors.Actor do
on: a.id == f.actor_id,
where: f.target_actor_id == ^actor_id
)
|> paginate(page, limit)
)
end
@@ -292,6 +338,22 @@ defmodule Mobilizon.Actors.Actor do
"""
@spec get_followings(struct(), number(), number()) :: list()
def get_followings(%Actor{id: actor_id} = _actor, page \\ nil, limit \\ nil) do
query =
from(
a in Actor,
join: f in Follower,
on: a.id == f.target_actor_id,
where: f.actor_id == ^actor_id
)
total = Task.async(fn -> Repo.aggregate(query, :count, :id) end)
elements = Task.async(fn -> Repo.all(paginate(query, page, limit)) end)
%{total: Task.await(total), elements: Task.await(elements)}
end
@spec get_full_followings(struct()) :: list()
def get_full_followings(%Actor{id: actor_id} = _actor) do
Repo.all(
from(
a in Actor,
@@ -299,7 +361,6 @@ defmodule Mobilizon.Actors.Actor do
on: a.id == f.target_actor_id,
where: f.actor_id == ^actor_id
)
|> paginate(page, limit)
)
end
@@ -390,6 +451,9 @@ defmodule Mobilizon.Actors.Actor do
end
end
@spec public_visibility?(struct()) :: boolean()
def public_visibility?(%Actor{visibility: visibility}), do: visibility in [:public, :unlisted]
@doc """
Return the preferred_username with the eventual @domain suffix if it's a distant actor
"""

View File

@@ -158,7 +158,8 @@ defmodule Mobilizon.Actors do
Repo.all(
from(
a in Actor,
where: a.type == ^:Group
where: a.type == ^:Group,
where: a.visibility in [^:public, ^:unlisted]
)
|> paginate(page, limit)
)

View File

@@ -19,6 +19,8 @@ defmodule Mobilizon.Events.Comment do
alias Mobilizon.Events.Event
alias Mobilizon.Actors.Actor
alias Mobilizon.Events.Comment
alias MobilizonWeb.Router.Helpers, as: Routes
alias MobilizonWeb.Endpoint
schema "comments" do
field(:text, :string)
@@ -46,7 +48,7 @@ defmodule Mobilizon.Events.Comment do
url =
if Map.has_key?(attrs, "url"),
do: attrs["url"],
else: "#{MobilizonWeb.Endpoint.url()}/comments/#{uuid}"
else: Routes.page_url(Endpoint, :comment, uuid)
comment
|> Ecto.Changeset.cast(attrs, [