forked from AkkomaGang/akkoma
Resolve merge conflict
This commit is contained in:
commit
dec23500d8
56 changed files with 761 additions and 123 deletions
|
@ -8,6 +8,7 @@ variables:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
DB_HOST: postgres
|
DB_HOST: postgres
|
||||||
|
MIX_ENV: test
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
key: ${CI_COMMIT_REF_SLUG}
|
key: ${CI_COMMIT_REF_SLUG}
|
||||||
|
@ -23,15 +24,15 @@ before_script:
|
||||||
- mix local.rebar --force
|
- mix local.rebar --force
|
||||||
- mix deps.get
|
- mix deps.get
|
||||||
- mix compile --force
|
- mix compile --force
|
||||||
- MIX_ENV=test mix ecto.create
|
- mix ecto.create
|
||||||
- MIX_ENV=test mix ecto.migrate
|
- mix ecto.migrate
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
stage: lint
|
stage: lint
|
||||||
script:
|
script:
|
||||||
- MIX_ENV=test mix format --check-formatted
|
- mix format --check-formatted
|
||||||
|
|
||||||
unit-testing:
|
unit-testing:
|
||||||
stage: test
|
stage: test
|
||||||
script:
|
script:
|
||||||
- MIX_ENV=test mix test --trace
|
- mix test --trace
|
||||||
|
|
|
@ -30,7 +30,7 @@ While we don't provide docker files, other people have written very good ones. T
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* Postgresql version 9.6 or newer
|
* Postgresql version 9.6 or newer
|
||||||
* Elixir version 1.5 or newer. If your distribution only has an old version available, check [Elixir's install page](https://elixir-lang.org/install.html) or use a tool like [asdf](https://github.com/asdf-vm/asdf).
|
* Elixir version 1.7 or newer. If your distribution only has an old version available, check [Elixir's install page](https://elixir-lang.org/install.html) or use a tool like [asdf](https://github.com/asdf-vm/asdf).
|
||||||
* Build-essential tools
|
* Build-essential tools
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
|
@ -98,7 +98,8 @@
|
||||||
name: "Pleroma",
|
name: "Pleroma",
|
||||||
email: "example@example.com",
|
email: "example@example.com",
|
||||||
description: "A Pleroma instance, an alternative fediverse server",
|
description: "A Pleroma instance, an alternative fediverse server",
|
||||||
limit: 5000,
|
limit: 5_000,
|
||||||
|
remote_limit: 100_000,
|
||||||
upload_limit: 16_000_000,
|
upload_limit: 16_000_000,
|
||||||
avatar_upload_limit: 2_000_000,
|
avatar_upload_limit: 2_000_000,
|
||||||
background_upload_limit: 4_000_000,
|
background_upload_limit: 4_000_000,
|
||||||
|
@ -137,8 +138,8 @@
|
||||||
logo_mask: true,
|
logo_mask: true,
|
||||||
logo_margin: "0.1em",
|
logo_margin: "0.1em",
|
||||||
background: "/static/aurora_borealis.jpg",
|
background: "/static/aurora_borealis.jpg",
|
||||||
redirect_root_no_login: "/~/main/all",
|
redirect_root_no_login: "/main/all",
|
||||||
redirect_root_login: "/~/main/friends",
|
redirect_root_login: "/main/friends",
|
||||||
show_instance_panel: true,
|
show_instance_panel: true,
|
||||||
scope_options_enabled: false,
|
scope_options_enabled: false,
|
||||||
formatting_options_enabled: false,
|
formatting_options_enabled: false,
|
||||||
|
@ -220,6 +221,37 @@
|
||||||
credentials: true,
|
credentials: true,
|
||||||
headers: ["Authorization", "Content-Type", "Idempotency-Key"]
|
headers: ["Authorization", "Content-Type", "Idempotency-Key"]
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.User,
|
||||||
|
restricted_nicknames: [
|
||||||
|
"about",
|
||||||
|
"~",
|
||||||
|
"main",
|
||||||
|
"users",
|
||||||
|
"settings",
|
||||||
|
"objects",
|
||||||
|
"activities",
|
||||||
|
"web",
|
||||||
|
"registration",
|
||||||
|
"friend-requests",
|
||||||
|
"pleroma",
|
||||||
|
"api",
|
||||||
|
"tag",
|
||||||
|
"notice",
|
||||||
|
"status",
|
||||||
|
"user-search",
|
||||||
|
"ostatus_subscribe",
|
||||||
|
"oauth",
|
||||||
|
"push",
|
||||||
|
"relay",
|
||||||
|
"inbox",
|
||||||
|
".well-known",
|
||||||
|
"nodeinfo",
|
||||||
|
"auth",
|
||||||
|
"proxy",
|
||||||
|
"dev",
|
||||||
|
"internal"
|
||||||
|
]
|
||||||
|
|
||||||
# Import environment specific config. This must remain at the bottom
|
# Import environment specific config. This must remain at the bottom
|
||||||
# of this file so it overrides the configuration defined above.
|
# of this file so it overrides the configuration defined above.
|
||||||
import_config "#{Mix.env()}.exs"
|
import_config "#{Mix.env()}.exs"
|
||||||
|
|
|
@ -63,6 +63,7 @@ config :pleroma, Pleroma.Mailer,
|
||||||
* `email`: Email used to reach an Administrator/Moderator of the instance
|
* `email`: Email used to reach an Administrator/Moderator of the instance
|
||||||
* `description`: The instance’s description, can be seen in nodeinfo and ``/api/v1/instance``
|
* `description`: The instance’s description, can be seen in nodeinfo and ``/api/v1/instance``
|
||||||
* `limit`: Posts character limit (CW/Subject included in the counter)
|
* `limit`: Posts character limit (CW/Subject included in the counter)
|
||||||
|
* `remote_limit`: Hard character limit beyond which remote posts will be dropped.
|
||||||
* `upload_limit`: File size limit of uploads (except for avatar, background, banner)
|
* `upload_limit`: File size limit of uploads (except for avatar, background, banner)
|
||||||
* `avatar_upload_limit`: File size limit of user’s profile avatars
|
* `avatar_upload_limit`: File size limit of user’s profile avatars
|
||||||
* `background_upload_limit`: File size limit of user’s profile backgrounds
|
* `background_upload_limit`: File size limit of user’s profile backgrounds
|
||||||
|
|
|
@ -21,6 +21,8 @@ ProtectSystem=full
|
||||||
PrivateDevices=false
|
PrivateDevices=false
|
||||||
; Ensures that the service process and all its children can never gain new privileges through execve().
|
; Ensures that the service process and all its children can never gain new privileges through execve().
|
||||||
NoNewPrivileges=true
|
NoNewPrivileges=true
|
||||||
|
; Drops the sysadmin capability from the daemon.
|
||||||
|
CapabilityBoundingSet=~CAP_SYS_ADMIN
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|
|
@ -75,7 +75,7 @@ def run(["gen" | rest]) do
|
||||||
name =
|
name =
|
||||||
Common.get_option(
|
Common.get_option(
|
||||||
options,
|
options,
|
||||||
:name,
|
:instance_name,
|
||||||
"What is the name of your instance? (e.g. Pleroma/Soykaf)"
|
"What is the name of your instance? (e.g. Pleroma/Soykaf)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ defmodule Pleroma.Activity do
|
||||||
alias Pleroma.{Repo, Activity, Notification}
|
alias Pleroma.{Repo, Activity, Notification}
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{}
|
||||||
|
|
||||||
# https://github.com/tootsuite/mastodon/blob/master/app/models/notification.rb#L19
|
# https://github.com/tootsuite/mastodon/blob/master/app/models/notification.rb#L19
|
||||||
@mastodon_notification_types %{
|
@mastodon_notification_types %{
|
||||||
"Create" => "mention",
|
"Create" => "mention",
|
||||||
|
|
|
@ -10,6 +10,8 @@ defmodule Pleroma.HTTP do
|
||||||
alias Pleroma.HTTP.Connection
|
alias Pleroma.HTTP.Connection
|
||||||
alias Pleroma.HTTP.RequestBuilder, as: Builder
|
alias Pleroma.HTTP.RequestBuilder, as: Builder
|
||||||
|
|
||||||
|
@type t :: __MODULE__
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Builds and perform http request.
|
Builds and perform http request.
|
||||||
|
|
||||||
|
|
|
@ -80,9 +80,8 @@ def get(%{id: user_id} = _user, id) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear(user) do
|
def clear(user) do
|
||||||
query = from(n in Notification, where: n.user_id == ^user.id)
|
from(n in Notification, where: n.user_id == ^user.id)
|
||||||
|
|> Repo.delete_all()
|
||||||
Repo.delete_all(query)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def dismiss(%{id: user_id} = _user, id) do
|
def dismiss(%{id: user_id} = _user, id) do
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
|
|
||||||
defmodule Pleroma.Object do
|
defmodule Pleroma.Object do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
alias Pleroma.{Repo, Object, User, Activity, HTML}
|
alias Pleroma.{Repo, Object, User, Activity, HTML, ObjectTombstone}
|
||||||
|
alias Pleroma.{Repo, Object, User, Activity, ObjectTombstone}
|
||||||
import Ecto.{Query, Changeset}
|
import Ecto.{Query, Changeset}
|
||||||
|
|
||||||
schema "objects" do
|
schema "objects" do
|
||||||
|
@ -66,8 +67,25 @@ def context_mapping(context) do
|
||||||
Object.change(%Object{}, %{data: %{"id" => context}})
|
Object.change(%Object{}, %{data: %{"id" => context}})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def make_tombstone(%Object{data: %{"id" => id, "type" => type}}, deleted \\ DateTime.utc_now()) do
|
||||||
|
%ObjectTombstone{
|
||||||
|
id: id,
|
||||||
|
formerType: type,
|
||||||
|
deleted: deleted
|
||||||
|
}
|
||||||
|
|> Map.from_struct()
|
||||||
|
end
|
||||||
|
|
||||||
|
def swap_object_with_tombstone(object) do
|
||||||
|
tombstone = make_tombstone(object)
|
||||||
|
|
||||||
|
object
|
||||||
|
|> Object.change(%{data: tombstone})
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
def delete(%Object{data: %{"id" => id}} = object) do
|
def delete(%Object{data: %{"id" => id}} = object) do
|
||||||
with Repo.delete(object),
|
with {:ok, _obj} = swap_object_with_tombstone(object),
|
||||||
Repo.delete_all(Activity.all_non_create_by_object_ap_id_q(id)),
|
Repo.delete_all(Activity.all_non_create_by_object_ap_id_q(id)),
|
||||||
{:ok, true} <- Cachex.del(:object_cache, "object:#{id}") do
|
{:ok, true} <- Cachex.del(:object_cache, "object:#{id}") do
|
||||||
{:ok, object}
|
{:ok, object}
|
||||||
|
|
4
lib/pleroma/object_tombstone.ex
Normal file
4
lib/pleroma/object_tombstone.ex
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
defmodule Pleroma.ObjectTombstone do
|
||||||
|
@enforce_keys [:id, :formerType, :deleted]
|
||||||
|
defstruct [:id, :formerType, :deleted, type: "Tombstone"]
|
||||||
|
end
|
|
@ -13,6 +13,8 @@ defmodule Pleroma.User do
|
||||||
alias Pleroma.Web.{OStatus, Websub, OAuth}
|
alias Pleroma.Web.{OStatus, Websub, OAuth}
|
||||||
alias Pleroma.Web.ActivityPub.{Utils, ActivityPub}
|
alias Pleroma.Web.ActivityPub.{Utils, ActivityPub}
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
@type t :: %__MODULE__{}
|
@type t :: %__MODULE__{}
|
||||||
|
|
||||||
@email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
|
@email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
|
||||||
|
@ -47,6 +49,14 @@ def auth_active?(%User{} = user) do
|
||||||
!Pleroma.Config.get([:instance, :account_activation_required])
|
!Pleroma.Config.get([:instance, :account_activation_required])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def remote_or_auth_active?(%User{} = user), do: !user.local || auth_active?(user)
|
||||||
|
|
||||||
|
def visible_for?(%User{} = user, for_user \\ nil) do
|
||||||
|
User.remote_or_auth_active?(user) || (for_user && for_user.id == user.id) ||
|
||||||
|
User.superuser?(for_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def superuser?(nil), do: false
|
||||||
def superuser?(%User{} = user), do: user.info && User.Info.superuser?(user.info)
|
def superuser?(%User{} = user), do: user.info && User.Info.superuser?(user.info)
|
||||||
|
|
||||||
def avatar_url(user) do
|
def avatar_url(user) do
|
||||||
|
@ -197,6 +207,7 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do
|
||||||
|> validate_confirmation(:password)
|
|> validate_confirmation(:password)
|
||||||
|> unique_constraint(:email)
|
|> unique_constraint(:email)
|
||||||
|> unique_constraint(:nickname)
|
|> unique_constraint(:nickname)
|
||||||
|
|> validate_exclusion(:nickname, Pleroma.Config.get([Pleroma.User, :restricted_nicknames]))
|
||||||
|> validate_format(:nickname, local_nickname_regex())
|
|> validate_format(:nickname, local_nickname_regex())
|
||||||
|> validate_format(:email, @email_regex)
|
|> validate_format(:email, @email_regex)
|
||||||
|> validate_length(:bio, max: 1000)
|
|> validate_length(:bio, max: 1000)
|
||||||
|
@ -330,6 +341,24 @@ def following?(%User{} = follower, %User{} = followed) do
|
||||||
Enum.member?(follower.following, followed.follower_address)
|
Enum.member?(follower.following, followed.follower_address)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def follow_import(%User{} = follower, followed_identifiers)
|
||||||
|
when is_list(followed_identifiers) do
|
||||||
|
Enum.map(
|
||||||
|
followed_identifiers,
|
||||||
|
fn followed_identifier ->
|
||||||
|
with %User{} = followed <- get_or_fetch(followed_identifier),
|
||||||
|
{:ok, follower} <- maybe_direct_follow(follower, followed),
|
||||||
|
{:ok, _} <- ActivityPub.follow(follower, followed) do
|
||||||
|
followed
|
||||||
|
else
|
||||||
|
err ->
|
||||||
|
Logger.debug("follow_import failed for #{followed_identifier} with: #{inspect(err)}")
|
||||||
|
err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def locked?(%User{} = user) do
|
def locked?(%User{} = user) do
|
||||||
user.info.locked || false
|
user.info.locked || false
|
||||||
end
|
end
|
||||||
|
@ -366,7 +395,11 @@ def get_cached_by_nickname(nickname) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_by_nickname(nickname) do
|
def get_by_nickname(nickname) do
|
||||||
Repo.get_by(User, nickname: nickname)
|
Repo.get_by(User, nickname: nickname) ||
|
||||||
|
if Regex.match?(~r(@#{Pleroma.Web.Endpoint.host()})i, nickname) do
|
||||||
|
[local_nickname, _] = String.split(nickname, "@")
|
||||||
|
Repo.get_by(User, nickname: local_nickname)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_by_nickname_or_email(nickname_or_email) do
|
def get_by_nickname_or_email(nickname_or_email) do
|
||||||
|
@ -595,6 +628,23 @@ def search(query, resolve \\ false) do
|
||||||
Repo.all(q)
|
Repo.all(q)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_identifiers) do
|
||||||
|
Enum.map(
|
||||||
|
blocked_identifiers,
|
||||||
|
fn blocked_identifier ->
|
||||||
|
with %User{} = blocked <- get_or_fetch(blocked_identifier),
|
||||||
|
{:ok, blocker} <- block(blocker, blocked),
|
||||||
|
{:ok, _} <- ActivityPub.block(blocker, blocked) do
|
||||||
|
blocked
|
||||||
|
else
|
||||||
|
err ->
|
||||||
|
Logger.debug("blocks_import failed for #{blocked_identifier} with: #{inspect(err)}")
|
||||||
|
err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def block(blocker, %User{ap_id: ap_id} = blocked) do
|
def block(blocker, %User{ap_id: ap_id} = blocked) do
|
||||||
# sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213)
|
# sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213)
|
||||||
blocker =
|
blocker =
|
||||||
|
@ -648,6 +698,9 @@ def blocks?(user, %{ap_id: ap_id}) do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def blocked_users(user),
|
||||||
|
do: Repo.all(from(u in User, where: u.ap_id in ^user.info.blocks))
|
||||||
|
|
||||||
def block_domain(user, domain) do
|
def block_domain(user, domain) do
|
||||||
info_cng =
|
info_cng =
|
||||||
user.info
|
user.info
|
||||||
|
|
|
@ -56,10 +56,18 @@ defp check_actor_is_active(actor) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp check_remote_limit(%{"object" => %{"content" => content}}) do
|
||||||
|
limit = Pleroma.Config.get([:instance, :remote_limit])
|
||||||
|
String.length(content) <= limit
|
||||||
|
end
|
||||||
|
|
||||||
|
defp check_remote_limit(_), do: true
|
||||||
|
|
||||||
def insert(map, local \\ true) when is_map(map) do
|
def insert(map, local \\ true) when is_map(map) do
|
||||||
with nil <- Activity.normalize(map),
|
with nil <- Activity.normalize(map),
|
||||||
map <- lazy_put_activity_defaults(map),
|
map <- lazy_put_activity_defaults(map),
|
||||||
:ok <- check_actor_is_active(map["actor"]),
|
:ok <- check_actor_is_active(map["actor"]),
|
||||||
|
{_, true} <- {:remote_limit_error, check_remote_limit(map)},
|
||||||
{:ok, map} <- MRF.filter(map),
|
{:ok, map} <- MRF.filter(map),
|
||||||
:ok <- insert_full_object(map) do
|
:ok <- insert_full_object(map) do
|
||||||
{recipients, _, _} = get_recipients(map)
|
{recipients, _, _} = get_recipients(map)
|
||||||
|
@ -503,6 +511,12 @@ defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or
|
||||||
|
|
||||||
defp restrict_replies(query, _), do: query
|
defp restrict_replies(query, _), do: query
|
||||||
|
|
||||||
|
defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
|
||||||
|
from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict_reblogs(query, _), do: query
|
||||||
|
|
||||||
# Only search through last 100_000 activities by default
|
# Only search through last 100_000 activities by default
|
||||||
defp restrict_recent(query, %{"whole_db" => true}), do: query
|
defp restrict_recent(query, %{"whole_db" => true}), do: query
|
||||||
|
|
||||||
|
@ -561,6 +575,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
|> restrict_media(opts)
|
|> restrict_media(opts)
|
||||||
|> restrict_visibility(opts)
|
|> restrict_visibility(opts)
|
||||||
|> restrict_replies(opts)
|
|> restrict_replies(opts)
|
||||||
|
|> restrict_reblogs(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_activities(recipients, opts \\ %{}) do
|
def fetch_activities(recipients, opts \\ %{}) do
|
||||||
|
|
|
@ -4,11 +4,12 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
alias Pleroma.{User, Object}
|
alias Pleroma.{Activity, User, Object}
|
||||||
alias Pleroma.Web.ActivityPub.{ObjectView, UserView}
|
alias Pleroma.Web.ActivityPub.{ObjectView, UserView}
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
@ -93,19 +94,15 @@ def followers(conn, %{"nickname" => nickname}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def outbox(conn, %{"nickname" => nickname, "max_id" => max_id}) do
|
def outbox(conn, %{"nickname" => nickname} = params) do
|
||||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||||
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
|
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
|
||||||
conn
|
conn
|
||||||
|> put_resp_header("content-type", "application/activity+json")
|
|> put_resp_header("content-type", "application/activity+json")
|
||||||
|> json(UserView.render("outbox.json", %{user: user, max_id: max_id}))
|
|> json(UserView.render("outbox.json", %{user: user, max_id: params["max_id"]}))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def outbox(conn, %{"nickname" => nickname}) do
|
|
||||||
outbox(conn, %{"nickname" => nickname, "max_id" => nil})
|
|
||||||
end
|
|
||||||
|
|
||||||
def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do
|
def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do
|
||||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||||
true <- Utils.recipient_in_message(user.ap_id, params),
|
true <- Utils.recipient_in_message(user.ap_id, params),
|
||||||
|
@ -156,6 +153,57 @@ def relay(conn, _params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def read_inbox(%{assigns: %{user: user}} = conn, %{"nickname" => nickname} = params) do
|
||||||
|
if nickname == user.nickname do
|
||||||
|
conn
|
||||||
|
|> put_resp_header("content-type", "application/activity+json")
|
||||||
|
|> json(UserView.render("inbox.json", %{user: user, max_id: params["max_id"]}))
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
|> put_status(:forbidden)
|
||||||
|
|> json("can't read inbox of #{nickname} as #{user.nickname}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_outbox(
|
||||||
|
%{assigns: %{user: user}} = conn,
|
||||||
|
%{"nickname" => nickname, "type" => "Create"} = params
|
||||||
|
) do
|
||||||
|
if nickname == user.nickname do
|
||||||
|
actor = user.ap_id()
|
||||||
|
|
||||||
|
params =
|
||||||
|
params
|
||||||
|
|> Map.drop(["id"])
|
||||||
|
|> Map.put("actor", actor)
|
||||||
|
|> Transmogrifier.fix_addressing()
|
||||||
|
|
||||||
|
object =
|
||||||
|
params["object"]
|
||||||
|
|> Map.merge(Map.take(params, ["to", "cc"]))
|
||||||
|
|> Map.put("attributedTo", actor)
|
||||||
|
|> Transmogrifier.fix_object()
|
||||||
|
|
||||||
|
with {:ok, %Activity{} = activity} <-
|
||||||
|
ActivityPub.create(%{
|
||||||
|
to: params["to"],
|
||||||
|
actor: user,
|
||||||
|
context: object["context"],
|
||||||
|
object: object,
|
||||||
|
additional: Map.take(params, ["cc"])
|
||||||
|
}) do
|
||||||
|
conn
|
||||||
|
|> put_status(:created)
|
||||||
|
|> put_resp_header("location", activity.data["id"])
|
||||||
|
|> json(activity.data)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
|> put_status(:forbidden)
|
||||||
|
|> json("can't update outbox of #{nickname} as #{user.nickname}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def errors(conn, {:error, :not_found}) do
|
def errors(conn, {:error, :not_found}) do
|
||||||
conn
|
conn
|
||||||
|> put_status(404)
|
|> put_status(404)
|
||||||
|
|
|
@ -176,6 +176,53 @@ def render("outbox.json", %{user: user, max_id: max_qid}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def render("inbox.json", %{user: user, max_id: max_qid}) do
|
||||||
|
params = %{
|
||||||
|
"limit" => "10"
|
||||||
|
}
|
||||||
|
|
||||||
|
params =
|
||||||
|
if max_qid != nil do
|
||||||
|
Map.put(params, "max_id", max_qid)
|
||||||
|
else
|
||||||
|
params
|
||||||
|
end
|
||||||
|
|
||||||
|
activities = ActivityPub.fetch_activities([user.ap_id | user.following], params)
|
||||||
|
|
||||||
|
min_id = Enum.at(Enum.reverse(activities), 0).id
|
||||||
|
max_id = Enum.at(activities, 0).id
|
||||||
|
|
||||||
|
collection =
|
||||||
|
Enum.map(activities, fn act ->
|
||||||
|
{:ok, data} = Transmogrifier.prepare_outgoing(act.data)
|
||||||
|
data
|
||||||
|
end)
|
||||||
|
|
||||||
|
iri = "#{user.ap_id}/inbox"
|
||||||
|
|
||||||
|
page = %{
|
||||||
|
"id" => "#{iri}?max_id=#{max_id}",
|
||||||
|
"type" => "OrderedCollectionPage",
|
||||||
|
"partOf" => iri,
|
||||||
|
"totalItems" => -1,
|
||||||
|
"orderedItems" => collection,
|
||||||
|
"next" => "#{iri}?max_id=#{min_id - 1}"
|
||||||
|
}
|
||||||
|
|
||||||
|
if max_qid == nil do
|
||||||
|
%{
|
||||||
|
"id" => iri,
|
||||||
|
"type" => "OrderedCollection",
|
||||||
|
"totalItems" => -1,
|
||||||
|
"first" => page
|
||||||
|
}
|
||||||
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
|
else
|
||||||
|
page |> Map.merge(Utils.make_json_ld_header())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def collection(collection, iri, page, show_items \\ true, total \\ nil) do
|
def collection(collection, iri, page, show_items \\ true, total \\ nil) do
|
||||||
offset = (page - 1) * 10
|
offset = (page - 1) * 10
|
||||||
items = Enum.slice(collection, offset, 10)
|
items = Enum.slice(collection, offset, 10)
|
||||||
|
|
|
@ -102,7 +102,7 @@ def post(user, %{"status" => status} = data) do
|
||||||
attachments,
|
attachments,
|
||||||
tags,
|
tags,
|
||||||
get_content_type(data["content_type"]),
|
get_content_type(data["content_type"]),
|
||||||
data["no_attachment_links"]
|
Enum.member?([true, "true"], data["no_attachment_links"])
|
||||||
),
|
),
|
||||||
context <- make_context(inReplyTo),
|
context <- make_context(inReplyTo),
|
||||||
cw <- data["spoiler_text"],
|
cw <- data["spoiler_text"],
|
||||||
|
|
|
@ -704,11 +704,9 @@ def unblock(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: Use proper query
|
|
||||||
def blocks(%{assigns: %{user: user}} = conn, _) do
|
def blocks(%{assigns: %{user: user}} = conn, _) do
|
||||||
with blocked_users <- user.info.blocks || [],
|
with blocked_accounts <- User.blocked_users(user) do
|
||||||
accounts <- Enum.map(blocked_users, fn ap_id -> User.get_cached_by_ap_id(ap_id) end) do
|
res = AccountView.render("accounts.json", users: blocked_accounts, for: user, as: :user)
|
||||||
res = AccountView.render("accounts.json", users: accounts, for: user, as: :user)
|
|
||||||
json(conn, res)
|
json(conn, res)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,10 +11,55 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
|
|
||||||
def render("accounts.json", %{users: users} = opts) do
|
def render("accounts.json", %{users: users} = opts) do
|
||||||
render_many(users, AccountView, "account.json", opts)
|
users
|
||||||
|
|> render_many(AccountView, "account.json", opts)
|
||||||
|
|> Enum.filter(&Enum.any?/1)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("account.json", %{user: user} = opts) do
|
def render("account.json", %{user: user} = opts) do
|
||||||
|
if User.visible_for?(user, opts[:for]),
|
||||||
|
do: do_render("account.json", opts),
|
||||||
|
else: %{}
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("mention.json", %{user: user}) do
|
||||||
|
%{
|
||||||
|
id: to_string(user.id),
|
||||||
|
acct: user.nickname,
|
||||||
|
username: username_from_nickname(user.nickname),
|
||||||
|
url: user.ap_id
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("relationship.json", %{user: user, target: target}) do
|
||||||
|
follow_activity = Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, target)
|
||||||
|
|
||||||
|
requested =
|
||||||
|
if follow_activity do
|
||||||
|
follow_activity.data["state"] == "pending"
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
%{
|
||||||
|
id: to_string(target.id),
|
||||||
|
following: User.following?(user, target),
|
||||||
|
followed_by: User.following?(target, user),
|
||||||
|
blocking: User.blocks?(user, target),
|
||||||
|
muting: false,
|
||||||
|
muting_notifications: false,
|
||||||
|
requested: requested,
|
||||||
|
domain_blocking: false,
|
||||||
|
showing_reblogs: false,
|
||||||
|
endorsed: false
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("relationships.json", %{user: user, targets: targets}) do
|
||||||
|
render_many(targets, AccountView, "relationship.json", user: user, as: :target)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_render("account.json", %{user: user} = opts) do
|
||||||
image = User.avatar_url(user) |> MediaProxy.url()
|
image = User.avatar_url(user) |> MediaProxy.url()
|
||||||
header = User.banner_url(user) |> MediaProxy.url()
|
header = User.banner_url(user) |> MediaProxy.url()
|
||||||
user_info = User.user_info(user)
|
user_info = User.user_info(user)
|
||||||
|
@ -72,43 +117,6 @@ def render("account.json", %{user: user} = opts) do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("mention.json", %{user: user}) do
|
|
||||||
%{
|
|
||||||
id: to_string(user.id),
|
|
||||||
acct: user.nickname,
|
|
||||||
username: username_from_nickname(user.nickname),
|
|
||||||
url: user.ap_id
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def render("relationship.json", %{user: user, target: target}) do
|
|
||||||
follow_activity = Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, target)
|
|
||||||
|
|
||||||
requested =
|
|
||||||
if follow_activity do
|
|
||||||
follow_activity.data["state"] == "pending"
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
%{
|
|
||||||
id: to_string(target.id),
|
|
||||||
following: User.following?(user, target),
|
|
||||||
followed_by: User.following?(target, user),
|
|
||||||
blocking: User.blocks?(user, target),
|
|
||||||
muting: false,
|
|
||||||
muting_notifications: false,
|
|
||||||
requested: requested,
|
|
||||||
domain_blocking: false,
|
|
||||||
showing_reblogs: false,
|
|
||||||
endorsed: false
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def render("relationships.json", %{user: user, targets: targets}) do
|
|
||||||
render_many(targets, AccountView, "relationship.json", user: user, as: :target)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp username_from_nickname(string) when is_binary(string) do
|
defp username_from_nickname(string) when is_binary(string) do
|
||||||
hd(String.split(string, "@"))
|
hd(String.split(string, "@"))
|
||||||
end
|
end
|
||||||
|
|
|
@ -138,7 +138,8 @@ def nodeinfo(conn, %{"version" => "2.0"}) do
|
||||||
},
|
},
|
||||||
accountActivationRequired: Keyword.get(instance, :account_activation_required, false),
|
accountActivationRequired: Keyword.get(instance, :account_activation_required, false),
|
||||||
invitesEnabled: Keyword.get(instance, :invites_enabled, false),
|
invitesEnabled: Keyword.get(instance, :invites_enabled, false),
|
||||||
features: features
|
features: features,
|
||||||
|
restrictedNicknames: Pleroma.Config.get([Pleroma.User, :restricted_nicknames])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,7 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
scope "/api/pleroma", Pleroma.Web.TwitterAPI do
|
scope "/api/pleroma", Pleroma.Web.TwitterAPI do
|
||||||
pipe_through(:authenticated_api)
|
pipe_through(:authenticated_api)
|
||||||
|
post("/blocks_import", UtilController, :blocks_import)
|
||||||
post("/follow_import", UtilController, :follow_import)
|
post("/follow_import", UtilController, :follow_import)
|
||||||
post("/change_password", UtilController, :change_password)
|
post("/change_password", UtilController, :change_password)
|
||||||
post("/delete_account", UtilController, :delete_account)
|
post("/delete_account", UtilController, :delete_account)
|
||||||
|
@ -281,6 +282,7 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
get("/statuses/followers", TwitterAPI.Controller, :followers)
|
get("/statuses/followers", TwitterAPI.Controller, :followers)
|
||||||
get("/statuses/friends", TwitterAPI.Controller, :friends)
|
get("/statuses/friends", TwitterAPI.Controller, :friends)
|
||||||
|
get("/statuses/blocks", TwitterAPI.Controller, :blocks)
|
||||||
get("/statuses/show/:id", TwitterAPI.Controller, :fetch_status)
|
get("/statuses/show/:id", TwitterAPI.Controller, :fetch_status)
|
||||||
get("/statusnet/conversation/:id", TwitterAPI.Controller, :fetch_conversation)
|
get("/statusnet/conversation/:id", TwitterAPI.Controller, :fetch_conversation)
|
||||||
|
|
||||||
|
@ -410,6 +412,27 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
pipeline :activitypub_client do
|
||||||
|
plug(:accepts, ["activity+json"])
|
||||||
|
plug(:fetch_session)
|
||||||
|
plug(Pleroma.Plugs.OAuthPlug)
|
||||||
|
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
||||||
|
plug(Pleroma.Plugs.UserFetcherPlug)
|
||||||
|
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
||||||
|
plug(Pleroma.Plugs.LegacyAuthenticationPlug)
|
||||||
|
plug(Pleroma.Plugs.AuthenticationPlug)
|
||||||
|
plug(Pleroma.Plugs.UserEnabledPlug)
|
||||||
|
plug(Pleroma.Plugs.SetUserSessionIdPlug)
|
||||||
|
plug(Pleroma.Plugs.EnsureUserKeyPlug)
|
||||||
|
end
|
||||||
|
|
||||||
|
scope "/", Pleroma.Web.ActivityPub do
|
||||||
|
pipe_through([:activitypub_client])
|
||||||
|
|
||||||
|
get("/users/:nickname/inbox", ActivityPubController, :read_inbox)
|
||||||
|
post("/users/:nickname/outbox", ActivityPubController, :update_outbox)
|
||||||
|
end
|
||||||
|
|
||||||
scope "/relay", Pleroma.Web.ActivityPub do
|
scope "/relay", Pleroma.Web.ActivityPub do
|
||||||
pipe_through(:ap_relay)
|
pipe_through(:ap_relay)
|
||||||
get("/", ActivityPubController, :relay)
|
get("/", ActivityPubController, :relay)
|
||||||
|
|
|
@ -161,16 +161,21 @@ def remote_users(%{data: %{"to" => to} = data}) do
|
||||||
|> Enum.filter(fn user -> user && !user.local end)
|
|> Enum.filter(fn user -> user && !user.local end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp send_to_user(%{info: %{salmon: salmon}}, feed, poster) do
|
# push an activity to remote accounts
|
||||||
|
#
|
||||||
|
defp send_to_user(%{info: %{salmon: salmon}}, feed, poster),
|
||||||
|
do: send_to_user(salmon, feed, poster)
|
||||||
|
|
||||||
|
defp send_to_user(url, feed, poster) when is_binary(url) do
|
||||||
with {:ok, %{status: code}} <-
|
with {:ok, %{status: code}} <-
|
||||||
poster.(
|
poster.(
|
||||||
salmon,
|
url,
|
||||||
feed,
|
feed,
|
||||||
[{"Content-Type", "application/magic-envelope+xml"}]
|
[{"Content-Type", "application/magic-envelope+xml"}]
|
||||||
) do
|
) do
|
||||||
Logger.debug(fn -> "Pushed to #{salmon}, code #{code}" end)
|
Logger.debug(fn -> "Pushed to #{url}, code #{code}" end)
|
||||||
else
|
else
|
||||||
e -> Logger.debug(fn -> "Pushing Salmon to #{salmon} failed, #{inspect(e)}" end)
|
e -> Logger.debug(fn -> "Pushing Salmon to #{url} failed, #{inspect(e)}" end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -184,6 +189,11 @@ defp send_to_user(_, _, _), do: nil
|
||||||
"Undo",
|
"Undo",
|
||||||
"Delete"
|
"Delete"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Publishes an activity to remote accounts
|
||||||
|
"""
|
||||||
|
@spec publish(User.t(), Pleroma.Activity.t(), Pleroma.HTTP.t()) :: none
|
||||||
def publish(user, activity, poster \\ &@httpoison.post/3)
|
def publish(user, activity, poster \\ &@httpoison.post/3)
|
||||||
|
|
||||||
def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity, poster)
|
def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity, poster)
|
||||||
|
|
|
@ -240,22 +240,23 @@ def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
|
||||||
follow_import(conn, %{"list" => File.read!(listfile.path)})
|
follow_import(conn, %{"list" => File.read!(listfile.path)})
|
||||||
end
|
end
|
||||||
|
|
||||||
def follow_import(%{assigns: %{user: user}} = conn, %{"list" => list}) do
|
def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do
|
||||||
Task.start(fn ->
|
with followed_identifiers <- String.split(list),
|
||||||
String.split(list)
|
{:ok, _} = Task.start(fn -> User.follow_import(follower, followed_identifiers) end) do
|
||||||
|> Enum.map(fn account ->
|
|
||||||
with %User{} = follower <- User.get_cached_by_ap_id(user.ap_id),
|
|
||||||
%User{} = followed <- User.get_or_fetch(account),
|
|
||||||
{:ok, follower} <- User.maybe_direct_follow(follower, followed) do
|
|
||||||
ActivityPub.follow(follower, followed)
|
|
||||||
else
|
|
||||||
err -> Logger.debug("follow_import: following #{account} failed with #{inspect(err)}")
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
json(conn, "job started")
|
json(conn, "job started")
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
|
||||||
|
blocks_import(conn, %{"list" => File.read!(listfile.path)})
|
||||||
|
end
|
||||||
|
|
||||||
|
def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
|
||||||
|
with blocked_identifiers <- String.split(list),
|
||||||
|
{:ok, _} = Task.start(fn -> User.blocks_import(blocker, blocked_identifiers) end) do
|
||||||
|
json(conn, "job started")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def change_password(%{assigns: %{user: user}} = conn, params) do
|
def change_password(%{assigns: %{user: user}} = conn, params) do
|
||||||
case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
|
case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
|
||||||
|
|
|
@ -130,6 +130,15 @@ def show_user(conn, params) do
|
||||||
def user_timeline(%{assigns: %{user: user}} = conn, params) do
|
def user_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
case TwitterAPI.get_user(user, params) do
|
case TwitterAPI.get_user(user, params) do
|
||||||
{:ok, target_user} ->
|
{:ok, target_user} ->
|
||||||
|
# Twitter and ActivityPub use a different name and sense for this parameter.
|
||||||
|
{include_rts, params} = Map.pop(params, "include_rts")
|
||||||
|
|
||||||
|
params =
|
||||||
|
case include_rts do
|
||||||
|
x when x == "false" or x == "0" -> Map.put(params, "exclude_reblogs", "true")
|
||||||
|
_ -> params
|
||||||
|
end
|
||||||
|
|
||||||
activities = ActivityPub.fetch_user_activities(target_user, user, params)
|
activities = ActivityPub.fetch_user_activities(target_user, user, params)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -498,6 +507,14 @@ def friends(%{assigns: %{user: for_user}} = conn, params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def blocks(%{assigns: %{user: user}} = conn, _params) do
|
||||||
|
with blocked_users <- User.blocked_users(user) do
|
||||||
|
conn
|
||||||
|
|> put_view(UserView)
|
||||||
|
|> render("index.json", %{users: blocked_users, for: user})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def friend_requests(conn, params) do
|
def friend_requests(conn, params) do
|
||||||
with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
|
with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
|
||||||
{:ok, friend_requests} <- User.get_follow_requests(user) do
|
{:ok, friend_requests} <- User.get_follow_requests(user) do
|
||||||
|
@ -653,7 +670,7 @@ defp forbidden_json_reply(conn, error_message) do
|
||||||
json_reply(conn, 403, json)
|
json_reply(conn, 403, json)
|
||||||
end
|
end
|
||||||
|
|
||||||
def only_if_public_instance(conn = %{conn: %{assigns: %{user: _user}}}, _), do: conn
|
def only_if_public_instance(%{assigns: %{user: %User{}}} = conn, _), do: conn
|
||||||
|
|
||||||
def only_if_public_instance(conn, _) do
|
def only_if_public_instance(conn, _) do
|
||||||
if Keyword.get(Application.get_env(:pleroma, :instance), :public) do
|
if Keyword.get(Application.get_env(:pleroma, :instance), :public) do
|
||||||
|
|
|
@ -15,18 +15,44 @@ def render("show.json", %{user: user = %User{}} = assigns) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("index.json", %{users: users, for: user}) do
|
def render("index.json", %{users: users, for: user}) do
|
||||||
render_many(users, Pleroma.Web.TwitterAPI.UserView, "user.json", for: user)
|
users
|
||||||
|
|> render_many(Pleroma.Web.TwitterAPI.UserView, "user.json", for: user)
|
||||||
|
|> Enum.filter(&Enum.any?/1)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("user.json", %{user: user = %User{}} = assigns) do
|
def render("user.json", %{user: user = %User{}} = assigns) do
|
||||||
|
if User.visible_for?(user, assigns[:for]),
|
||||||
|
do: do_render("user.json", assigns),
|
||||||
|
else: %{}
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("short.json", %{
|
||||||
|
user: %User{
|
||||||
|
nickname: nickname,
|
||||||
|
id: id,
|
||||||
|
ap_id: ap_id,
|
||||||
|
name: name
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
%{
|
||||||
|
"fullname" => name,
|
||||||
|
"id" => id,
|
||||||
|
"ostatus_uri" => ap_id,
|
||||||
|
"profile_url" => ap_id,
|
||||||
|
"screen_name" => nickname
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_render("user.json", %{user: user = %User{}} = assigns) do
|
||||||
|
for_user = assigns[:for]
|
||||||
image = User.avatar_url(user) |> MediaProxy.url()
|
image = User.avatar_url(user) |> MediaProxy.url()
|
||||||
|
|
||||||
{following, follows_you, statusnet_blocking} =
|
{following, follows_you, statusnet_blocking} =
|
||||||
if assigns[:for] do
|
if for_user do
|
||||||
{
|
{
|
||||||
User.following?(assigns[:for], user),
|
User.following?(for_user, user),
|
||||||
User.following?(user, assigns[:for]),
|
User.following?(user, for_user),
|
||||||
User.blocks?(assigns[:for], user)
|
User.blocks?(for_user, user)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{false, false, false}
|
{false, false, false}
|
||||||
|
@ -51,7 +77,7 @@ def render("user.json", %{user: user = %User{}} = assigns) do
|
||||||
data = %{
|
data = %{
|
||||||
"created_at" => user.inserted_at |> Utils.format_naive_asctime(),
|
"created_at" => user.inserted_at |> Utils.format_naive_asctime(),
|
||||||
"description" => HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
|
"description" => HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
|
||||||
"description_html" => HTML.filter_tags(user.bio, User.html_filter_policy(assigns[:for])),
|
"description_html" => HTML.filter_tags(user.bio, User.html_filter_policy(for_user)),
|
||||||
"favourites_count" => 0,
|
"favourites_count" => 0,
|
||||||
"followers_count" => user_info[:follower_count],
|
"followers_count" => user_info[:follower_count],
|
||||||
"following" => following,
|
"following" => following,
|
||||||
|
@ -97,23 +123,6 @@ def render("user.json", %{user: user = %User{}} = assigns) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("short.json", %{
|
|
||||||
user: %User{
|
|
||||||
nickname: nickname,
|
|
||||||
id: id,
|
|
||||||
ap_id: ap_id,
|
|
||||||
name: name
|
|
||||||
}
|
|
||||||
}) do
|
|
||||||
%{
|
|
||||||
"fullname" => name,
|
|
||||||
"id" => id,
|
|
||||||
"ostatus_uri" => ap_id,
|
|
||||||
"profile_url" => ap_id,
|
|
||||||
"screen_name" => nickname
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp image_url(%{"url" => [%{"href" => href} | _]}), do: href
|
defp image_url(%{"url" => [%{"href" => href} | _]}), do: href
|
||||||
defp image_url(_), do: nil
|
defp image_url(_), do: nil
|
||||||
|
|
||||||
|
|
4
mix.exs
4
mix.exs
|
@ -5,7 +5,7 @@ def project do
|
||||||
[
|
[
|
||||||
app: :pleroma,
|
app: :pleroma,
|
||||||
version: version("0.9.0"),
|
version: version("0.9.0"),
|
||||||
elixir: "~> 1.4",
|
elixir: "~> 1.7",
|
||||||
elixirc_paths: elixirc_paths(Mix.env()),
|
elixirc_paths: elixirc_paths(Mix.env()),
|
||||||
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
||||||
elixirc_options: [warnings_as_errors: true],
|
elixirc_options: [warnings_as_errors: true],
|
||||||
|
@ -71,7 +71,7 @@ defp deps do
|
||||||
{:crypt,
|
{:crypt,
|
||||||
git: "https://github.com/msantos/crypt", ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"},
|
git: "https://github.com/msantos/crypt", ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"},
|
||||||
{:cors_plug, "~> 1.5"},
|
{:cors_plug, "~> 1.5"},
|
||||||
{:ex_doc, "> 0.18.3 and < 0.20.0", only: :dev, runtime: false},
|
{:ex_doc, "~> 0.19", only: :dev, runtime: false},
|
||||||
{:web_push_encryption, "~> 0.2.1"},
|
{:web_push_encryption, "~> 0.2.1"},
|
||||||
{:swoosh, "~> 0.20"},
|
{:swoosh, "~> 0.20"},
|
||||||
{:gen_smtp, "~> 0.13"},
|
{:gen_smtp, "~> 0.13"},
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.285fa56c62b811bbd37880f7e2656b13.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.60e190da7cc4cc4711dc.js></script><script type=text/javascript src=/static/js/vendor.48d4753220bd83360796.js></script><script type=text/javascript src=/static/js/app.6cb7378f44092df9536a.js></script></body></html>
|
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.44bcebbab7b3203648fdb538eb16129b.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.18ee0a4963e1e9ec7ea6.js></script><script type=text/javascript src=/static/js/vendor.21f9327c919db89265c3.js></script><script type=text/javascript src=/static/js/app.f5ecd4e55f996aad6b8a.js></script></body></html>
|
|
@ -4,8 +4,8 @@
|
||||||
"logo": "/static/logo.png",
|
"logo": "/static/logo.png",
|
||||||
"logoMask": true,
|
"logoMask": true,
|
||||||
"logoMargin": ".1em",
|
"logoMargin": ".1em",
|
||||||
"redirectRootNoLogin": "/~/main/all",
|
"redirectRootNoLogin": "/main/all",
|
||||||
"redirectRootLogin": "/~/main/friends",
|
"redirectRootLogin": "/main/friends",
|
||||||
"chatDisabled": false,
|
"chatDisabled": false,
|
||||||
"showInstanceSpecificPanel": false,
|
"showInstanceSpecificPanel": false,
|
||||||
"scopeOptionsEnabled": false,
|
"scopeOptionsEnabled": false,
|
||||||
|
@ -16,5 +16,7 @@
|
||||||
"alwaysShowSubjectInput": true,
|
"alwaysShowSubjectInput": true,
|
||||||
"hidePostStats": false,
|
"hidePostStats": false,
|
||||||
"hideUserStats": false,
|
"hideUserStats": false,
|
||||||
"loginMethod": "password"
|
"loginMethod": "password",
|
||||||
|
"webPushNotifications": false,
|
||||||
|
"noAttachmentLinks": false
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
priv/static/static/css/app.44bcebbab7b3203648fdb538eb16129b.css
Normal file
BIN
priv/static/static/css/app.44bcebbab7b3203648fdb538eb16129b.css
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/app.f5ecd4e55f996aad6b8a.js
Normal file
BIN
priv/static/static/js/app.f5ecd4e55f996aad6b8a.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/app.f5ecd4e55f996aad6b8a.js.map
Normal file
BIN
priv/static/static/js/app.f5ecd4e55f996aad6b8a.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/manifest.18ee0a4963e1e9ec7ea6.js
Normal file
BIN
priv/static/static/js/manifest.18ee0a4963e1e9ec7ea6.js
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/vendor.21f9327c919db89265c3.js
Normal file
BIN
priv/static/static/js/vendor.21f9327c919db89265c3.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/vendor.21f9327c919db89265c3.js.map
Normal file
BIN
priv/static/static/js/vendor.21f9327c919db89265c3.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -4,18 +4,19 @@
|
||||||
|
|
||||||
defmodule Pleroma.ActivityTest do
|
defmodule Pleroma.ActivityTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
|
alias Pleroma.Activity
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
test "returns an activity by it's AP id" do
|
test "returns an activity by it's AP id" do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
found_activity = Pleroma.Activity.get_by_ap_id(activity.data["id"])
|
found_activity = Activity.get_by_ap_id(activity.data["id"])
|
||||||
|
|
||||||
assert activity == found_activity
|
assert activity == found_activity
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns activities by it's objects AP ids" do
|
test "returns activities by it's objects AP ids" do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
[found_activity] = Pleroma.Activity.all_by_object_ap_id(activity.data["object"]["id"])
|
[found_activity] = Activity.all_by_object_ap_id(activity.data["object"]["id"])
|
||||||
|
|
||||||
assert activity == found_activity
|
assert activity == found_activity
|
||||||
end
|
end
|
||||||
|
@ -23,8 +24,7 @@ test "returns activities by it's objects AP ids" do
|
||||||
test "returns the activity that created an object" do
|
test "returns the activity that created an object" do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
|
|
||||||
found_activity =
|
found_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"]["id"])
|
||||||
Pleroma.Activity.get_create_activity_by_object_ap_id(activity.data["object"]["id"])
|
|
||||||
|
|
||||||
assert activity == found_activity
|
assert activity == found_activity
|
||||||
end
|
end
|
||||||
|
|
9
test/fixtures/activitypub-client-post-activity.json
vendored
Normal file
9
test/fixtures/activitypub-client-post-activity.json
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"@context": ["https://www.w3.org/ns/activitystreams", {"@language": "en-GB"}],
|
||||||
|
"type": "Create",
|
||||||
|
"object": {
|
||||||
|
"type": "Note",
|
||||||
|
"content": "It's a note"
|
||||||
|
},
|
||||||
|
"to": ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
|
}
|
|
@ -36,6 +36,8 @@ test "deletes an object" do
|
||||||
found_object = Object.get_by_ap_id(object.data["id"])
|
found_object = Object.get_by_ap_id(object.data["id"])
|
||||||
|
|
||||||
refute object == found_object
|
refute object == found_object
|
||||||
|
|
||||||
|
assert found_object.data["type"] == "Tombstone"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "ensures cache is cleared for the object" do
|
test "ensures cache is cleared for the object" do
|
||||||
|
@ -51,6 +53,8 @@ test "ensures cache is cleared for the object" do
|
||||||
cached_object = Object.get_cached_by_ap_id(object.data["id"])
|
cached_object = Object.get_cached_by_ap_id(object.data["id"])
|
||||||
|
|
||||||
refute object == cached_object
|
refute object == cached_object
|
||||||
|
|
||||||
|
assert cached_object.data["type"] == "Tombstone"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -153,6 +153,20 @@ test "it requires an email, name, nickname and password, bio is optional" do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it restricts certain nicknames" do
|
||||||
|
[restricted_name | _] = Pleroma.Config.get([Pleroma.User, :restricted_nicknames])
|
||||||
|
|
||||||
|
assert is_bitstring(restricted_name)
|
||||||
|
|
||||||
|
params =
|
||||||
|
@full_user_data
|
||||||
|
|> Map.put(:nickname, restricted_name)
|
||||||
|
|
||||||
|
changeset = User.register_changeset(%User{}, params)
|
||||||
|
|
||||||
|
refute changeset.valid?
|
||||||
|
end
|
||||||
|
|
||||||
test "it sets the password_hash, ap_id and following fields" do
|
test "it sets the password_hash, ap_id and following fields" do
|
||||||
changeset = User.register_changeset(%User{}, @full_user_data)
|
changeset = User.register_changeset(%User{}, @full_user_data)
|
||||||
|
|
||||||
|
@ -264,6 +278,24 @@ test "gets an existing user, case insensitive" do
|
||||||
assert user == fetched_user
|
assert user == fetched_user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "gets an existing user by fully qualified nickname" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
fetched_user =
|
||||||
|
User.get_or_fetch_by_nickname(user.nickname <> "@" <> Pleroma.Web.Endpoint.host())
|
||||||
|
|
||||||
|
assert user == fetched_user
|
||||||
|
end
|
||||||
|
|
||||||
|
test "gets an existing user by fully qualified nickname, case insensitive" do
|
||||||
|
user = insert(:user, nickname: "nick")
|
||||||
|
casing_altered_fqn = String.upcase(user.nickname <> "@" <> Pleroma.Web.Endpoint.host())
|
||||||
|
|
||||||
|
fetched_user = User.get_or_fetch_by_nickname(casing_altered_fqn)
|
||||||
|
|
||||||
|
assert user == fetched_user
|
||||||
|
end
|
||||||
|
|
||||||
test "fetches an external user via ostatus if no user exists" do
|
test "fetches an external user via ostatus if no user exists" do
|
||||||
fetched_user = User.get_or_fetch_by_nickname("shp@social.heldscal.la")
|
fetched_user = User.get_or_fetch_by_nickname("shp@social.heldscal.la")
|
||||||
assert fetched_user.nickname == "shp@social.heldscal.la"
|
assert fetched_user.nickname == "shp@social.heldscal.la"
|
||||||
|
@ -471,6 +503,21 @@ test "it sets the info->follower_count property" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "follow_import" do
|
||||||
|
test "it imports user followings from list" do
|
||||||
|
[user1, user2, user3] = insert_list(3, :user)
|
||||||
|
|
||||||
|
identifiers = [
|
||||||
|
user2.ap_id,
|
||||||
|
user3.nickname
|
||||||
|
]
|
||||||
|
|
||||||
|
result = User.follow_import(user1, identifiers)
|
||||||
|
assert is_list(result)
|
||||||
|
assert result == [user2, user3]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "blocks" do
|
describe "blocks" do
|
||||||
test "it blocks people" do
|
test "it blocks people" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
@ -570,6 +617,21 @@ test "unblocks domains" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "blocks_import" do
|
||||||
|
test "it imports user blocks from list" do
|
||||||
|
[user1, user2, user3] = insert_list(3, :user)
|
||||||
|
|
||||||
|
identifiers = [
|
||||||
|
user2.ap_id,
|
||||||
|
user3.nickname
|
||||||
|
]
|
||||||
|
|
||||||
|
result = User.blocks_import(user1, identifiers)
|
||||||
|
assert is_list(result)
|
||||||
|
assert result == [user2, user3]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "get recipients from activity" do
|
test "get recipients from activity" do
|
||||||
actor = insert(:user)
|
actor = insert(:user)
|
||||||
user = insert(:user, local: true)
|
user = insert(:user, local: true)
|
||||||
|
|
|
@ -112,6 +112,32 @@ test "it inserts an incoming activity into the database", %{conn: conn} do
|
||||||
:timer.sleep(500)
|
:timer.sleep(500)
|
||||||
assert Activity.get_by_ap_id(data["id"])
|
assert Activity.get_by_ap_id(data["id"])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it rejects reads from other users", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
otheruser = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, otheruser)
|
||||||
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|
|> get("/users/#{user.nickname}/inbox")
|
||||||
|
|
||||||
|
assert json_response(conn, 403)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns a note activity in a collection", %{conn: conn} do
|
||||||
|
note_activity = insert(:direct_note_activity)
|
||||||
|
user = User.get_cached_by_ap_id(hd(note_activity.data["to"]))
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|
|> get("/users/#{user.nickname}/inbox")
|
||||||
|
|
||||||
|
assert response(conn, 200) =~ note_activity.data["object"]["content"]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "/users/:nickname/outbox" do
|
describe "/users/:nickname/outbox" do
|
||||||
|
@ -138,6 +164,34 @@ test "it returns an announce activity in a collection", %{conn: conn} do
|
||||||
|
|
||||||
assert response(conn, 200) =~ announce_activity.data["object"]
|
assert response(conn, 200) =~ announce_activity.data["object"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it rejects posts from other users", %{conn: conn} do
|
||||||
|
data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
|
||||||
|
user = insert(:user)
|
||||||
|
otheruser = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, otheruser)
|
||||||
|
|> put_req_header("content-type", "application/activity+json")
|
||||||
|
|> post("/users/#{user.nickname}/outbox", data)
|
||||||
|
|
||||||
|
assert json_response(conn, 403)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it inserts an incoming activity into the database", %{conn: conn} do
|
||||||
|
data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> put_req_header("content-type", "application/activity+json")
|
||||||
|
|> post("/users/#{user.nickname}/outbox", data)
|
||||||
|
|
||||||
|
result = json_response(conn, 201)
|
||||||
|
assert Activity.get_by_ap_id(result["id"])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "/users/:nickname/followers" do
|
describe "/users/:nickname/followers" do
|
||||||
|
|
|
@ -31,6 +31,24 @@ test "it returns a user" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "insertion" do
|
describe "insertion" do
|
||||||
|
test "drops activities beyond a certain limit" do
|
||||||
|
limit = Pleroma.Config.get([:instance, :remote_limit])
|
||||||
|
|
||||||
|
random_text =
|
||||||
|
:crypto.strong_rand_bytes(limit + 1)
|
||||||
|
|> Base.encode64()
|
||||||
|
|> binary_part(0, limit + 1)
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
"ok" => true,
|
||||||
|
"object" => %{
|
||||||
|
"content" => random_text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:error, {:remote_limit_error, _}} = ActivityPub.insert(data)
|
||||||
|
end
|
||||||
|
|
||||||
test "returns the activity if one with the same id is already in" do
|
test "returns the activity if one with the same id is already in" do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
{:ok, new_activity} = ActivityPub.insert(activity.data)
|
{:ok, new_activity} = ActivityPub.insert(activity.data)
|
||||||
|
@ -180,6 +198,16 @@ test "doesn't return blocked activities" do
|
||||||
assert Enum.member?(activities, activity_one)
|
assert Enum.member?(activities, activity_one)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "excludes reblogs on request" do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
|
||||||
|
{:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
|
||||||
|
|
||||||
|
[activity] = ActivityPub.fetch_user_activities(user, nil, %{"exclude_reblogs" => "true"})
|
||||||
|
|
||||||
|
assert activity == expected_activity
|
||||||
|
end
|
||||||
|
|
||||||
describe "public fetch activities" do
|
describe "public fetch activities" do
|
||||||
test "doesn't retrieve unlisted activities" do
|
test "doesn't retrieve unlisted activities" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
@ -482,7 +510,7 @@ test "it creates a delete activity and deletes the original object" do
|
||||||
|
|
||||||
assert Repo.get(Activity, delete.id) != nil
|
assert Repo.get(Activity, delete.id) != nil
|
||||||
|
|
||||||
assert Repo.get(Object, object.id) == nil
|
assert Repo.get(Object, object.id).data["type"] == "Tombstone"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -296,7 +296,7 @@ test "when you created it", %{conn: conn} do
|
||||||
|
|
||||||
assert %{} = json_response(conn, 200)
|
assert %{} = json_response(conn, 200)
|
||||||
|
|
||||||
assert Repo.get(Activity, activity.id) == nil
|
refute Repo.get(Activity, activity.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "when you didn't create it", %{conn: conn} do
|
test "when you didn't create it", %{conn: conn} do
|
||||||
|
@ -840,6 +840,26 @@ test "gets an users media", %{conn: conn} do
|
||||||
assert [%{"id" => id}] = json_response(conn, 200)
|
assert [%{"id" => id}] = json_response(conn, 200)
|
||||||
assert id == to_string(image_post.id)
|
assert id == to_string(image_post.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "gets a user's statuses without reblogs", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
{:ok, _, _} = CommonAPI.repeat(post.id, user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
|
||||||
|
|
||||||
|
assert [%{"id" => id}] = json_response(conn, 200)
|
||||||
|
assert id == to_string(post.id)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
|
||||||
|
|
||||||
|
assert [%{"id" => id}] = json_response(conn, 200)
|
||||||
|
assert id == to_string(post.id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "user relationships" do
|
describe "user relationships" do
|
||||||
|
|
|
@ -19,6 +19,17 @@ test "nodeinfo shows staff accounts", %{conn: conn} do
|
||||||
assert user.ap_id in result["metadata"]["staffAccounts"]
|
assert user.ap_id in result["metadata"]["staffAccounts"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "nodeinfo shows restricted nicknames", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> get("/nodeinfo/2.0.json")
|
||||||
|
|
||||||
|
assert result = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert Pleroma.Config.get([Pleroma.User, :restricted_nicknames]) ==
|
||||||
|
result["metadata"]["restrictedNicknames"]
|
||||||
|
end
|
||||||
|
|
||||||
test "returns 404 when federation is disabled", %{conn: conn} do
|
test "returns 404 when federation is disabled", %{conn: conn} do
|
||||||
instance =
|
instance =
|
||||||
Application.get_env(:pleroma, :instance)
|
Application.get_env(:pleroma, :instance)
|
||||||
|
|
|
@ -25,7 +25,7 @@ test "it removes the mentioned activity" do
|
||||||
|
|
||||||
refute Repo.get(Activity, note.id)
|
refute Repo.get(Activity, note.id)
|
||||||
refute Repo.get(Activity, like.id)
|
refute Repo.get(Activity, like.id)
|
||||||
refute Object.get_by_ap_id(note.data["object"]["id"])
|
assert Object.get_by_ap_id(note.data["object"]["id"]).data["type"] == "Tombstone"
|
||||||
assert Repo.get(Activity, second_note.id)
|
assert Repo.get(Activity, second_note.id)
|
||||||
assert Object.get_by_ap_id(second_note.data["object"]["id"])
|
assert Object.get_by_ap_id(second_note.data["object"]["id"])
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
alias Pleroma.{User, Repo}
|
alias Pleroma.{User, Repo, Object}
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.OStatus.ActivityRepresenter
|
alias Pleroma.Web.OStatus.ActivityRepresenter
|
||||||
|
|
||||||
|
@ -114,6 +114,22 @@ test "404s on nonexisting objects", %{conn: conn} do
|
||||||
|> response(404)
|
|> response(404)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "404s on deleted objects", %{conn: conn} do
|
||||||
|
note_activity = insert(:note_activity)
|
||||||
|
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["object"]["id"]))
|
||||||
|
object = Object.get_by_ap_id(note_activity.data["object"]["id"])
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> get("/objects/#{uuid}")
|
||||||
|
|> response(200)
|
||||||
|
|
||||||
|
Object.delete(object)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> get("/objects/#{uuid}")
|
||||||
|
|> response(404)
|
||||||
|
end
|
||||||
|
|
||||||
test "gets an activity", %{conn: conn} do
|
test "gets an activity", %{conn: conn} do
|
||||||
note_activity = insert(:note_activity)
|
note_activity = insert(:note_activity)
|
||||||
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
|
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
|
||||||
|
|
|
@ -112,6 +112,8 @@ test "with credentials", %{conn: conn, user: user} do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET /statuses/public_timeline.json" do
|
describe "GET /statuses/public_timeline.json" do
|
||||||
|
setup [:valid_user]
|
||||||
|
|
||||||
test "returns statuses", %{conn: conn} do
|
test "returns statuses", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
activities = ActivityBuilder.insert_list(30, %{}, %{user: user})
|
activities = ActivityBuilder.insert_list(30, %{}, %{user: user})
|
||||||
|
@ -145,14 +147,44 @@ test "returns 403 to unauthenticated request when the instance is not public", %
|
||||||
Application.put_env(:pleroma, :instance, instance)
|
Application.put_env(:pleroma, :instance, instance)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns 200 to authenticated request when the instance is not public",
|
||||||
|
%{conn: conn, user: user} do
|
||||||
|
instance =
|
||||||
|
Application.get_env(:pleroma, :instance)
|
||||||
|
|> Keyword.put(:public, false)
|
||||||
|
|
||||||
|
Application.put_env(:pleroma, :instance, instance)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> with_credentials(user.nickname, "test")
|
||||||
|
|> get("/api/statuses/public_timeline.json")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
instance =
|
||||||
|
Application.get_env(:pleroma, :instance)
|
||||||
|
|> Keyword.put(:public, true)
|
||||||
|
|
||||||
|
Application.put_env(:pleroma, :instance, instance)
|
||||||
|
end
|
||||||
|
|
||||||
test "returns 200 to unauthenticated request when the instance is public", %{conn: conn} do
|
test "returns 200 to unauthenticated request when the instance is public", %{conn: conn} do
|
||||||
conn
|
conn
|
||||||
|> get("/api/statuses/public_timeline.json")
|
|> get("/api/statuses/public_timeline.json")
|
||||||
|> json_response(200)
|
|> json_response(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns 200 to authenticated request when the instance is public",
|
||||||
|
%{conn: conn, user: user} do
|
||||||
|
conn
|
||||||
|
|> with_credentials(user.nickname, "test")
|
||||||
|
|> get("/api/statuses/public_timeline.json")
|
||||||
|
|> json_response(200)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET /statuses/public_and_external_timeline.json" do
|
describe "GET /statuses/public_and_external_timeline.json" do
|
||||||
|
setup [:valid_user]
|
||||||
|
|
||||||
test "returns 403 to unauthenticated request when the instance is not public", %{conn: conn} do
|
test "returns 403 to unauthenticated request when the instance is not public", %{conn: conn} do
|
||||||
instance =
|
instance =
|
||||||
Application.get_env(:pleroma, :instance)
|
Application.get_env(:pleroma, :instance)
|
||||||
|
@ -171,11 +203,39 @@ test "returns 403 to unauthenticated request when the instance is not public", %
|
||||||
Application.put_env(:pleroma, :instance, instance)
|
Application.put_env(:pleroma, :instance, instance)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns 200 to authenticated request when the instance is not public",
|
||||||
|
%{conn: conn, user: user} do
|
||||||
|
instance =
|
||||||
|
Application.get_env(:pleroma, :instance)
|
||||||
|
|> Keyword.put(:public, false)
|
||||||
|
|
||||||
|
Application.put_env(:pleroma, :instance, instance)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> with_credentials(user.nickname, "test")
|
||||||
|
|> get("/api/statuses/public_and_external_timeline.json")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
instance =
|
||||||
|
Application.get_env(:pleroma, :instance)
|
||||||
|
|> Keyword.put(:public, true)
|
||||||
|
|
||||||
|
Application.put_env(:pleroma, :instance, instance)
|
||||||
|
end
|
||||||
|
|
||||||
test "returns 200 to unauthenticated request when the instance is public", %{conn: conn} do
|
test "returns 200 to unauthenticated request when the instance is public", %{conn: conn} do
|
||||||
conn
|
conn
|
||||||
|> get("/api/statuses/public_and_external_timeline.json")
|
|> get("/api/statuses/public_and_external_timeline.json")
|
||||||
|> json_response(200)
|
|> json_response(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns 200 to authenticated request when the instance is public",
|
||||||
|
%{conn: conn, user: user} do
|
||||||
|
conn
|
||||||
|
|> with_credentials(user.nickname, "test")
|
||||||
|
|> get("/api/statuses/public_and_external_timeline.json")
|
||||||
|
|> json_response(200)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET /statuses/show/:id.json" do
|
describe "GET /statuses/show/:id.json" do
|
||||||
|
@ -519,6 +579,34 @@ test "with credentials screen_name", %{conn: conn, user: current_user} do
|
||||||
assert length(response) == 1
|
assert length(response) == 1
|
||||||
assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user})
|
assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "with credentials with user_id, excluding RTs", %{conn: conn, user: current_user} do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, activity} = ActivityBuilder.insert(%{"id" => 1, "type" => "Create"}, %{user: user})
|
||||||
|
{:ok, _} = ActivityBuilder.insert(%{"id" => 2, "type" => "Announce"}, %{user: user})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> with_credentials(current_user.nickname, "test")
|
||||||
|
|> get("/api/statuses/user_timeline.json", %{
|
||||||
|
"user_id" => user.id,
|
||||||
|
"include_rts" => "false"
|
||||||
|
})
|
||||||
|
|
||||||
|
response = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert length(response) == 1
|
||||||
|
assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> get("/api/statuses/user_timeline.json", %{"user_id" => user.id, "include_rts" => "0"})
|
||||||
|
|
||||||
|
response = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert length(response) == 1
|
||||||
|
assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /friendships/create.json" do
|
describe "POST /friendships/create.json" do
|
||||||
|
@ -1057,6 +1145,24 @@ test "it returns the followers for a hidden network if requested by the user the
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "GET /api/statuses/blocks" do
|
||||||
|
test "it returns the list of users blocked by requester", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, user} = User.block(user, other_user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/statuses/blocks")
|
||||||
|
|
||||||
|
expected = UserView.render("index.json", %{users: [other_user], for: user})
|
||||||
|
result = json_response(conn, 200)
|
||||||
|
assert Enum.sort(expected) == Enum.sort(result)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "GET /api/statuses/friends" do
|
describe "GET /api/statuses/friends" do
|
||||||
test "it returns the logged in user's friends", %{conn: conn} do
|
test "it returns the logged in user's friends", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
35
test/web/twitter_api/util_controller_test.exs
Normal file
35
test/web/twitter_api/util_controller_test.exs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/follow_import" do
|
||||||
|
test "it returns HTTP 200", %{conn: conn} do
|
||||||
|
user1 = insert(:user)
|
||||||
|
user2 = insert(:user)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user1)
|
||||||
|
|> post("/api/pleroma/follow_import", %{"list" => "#{user2.ap_id}"})
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
assert response == "job started"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/blocks_import" do
|
||||||
|
test "it returns HTTP 200", %{conn: conn} do
|
||||||
|
user1 = insert(:user)
|
||||||
|
user2 = insert(:user)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user1)
|
||||||
|
|> post("/api/pleroma/blocks_import", %{"list" => "#{user2.ap_id}"})
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
assert response == "job started"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue