Merge remote-tracking branch 'origin/develop' into shigusegubu
* origin/develop: (62 commits) update pleroma frontend tests: add regression test MRF: anti followbot: gracefully handle accounts without a display name Adjust delete activity audience to match the deleted object Revert existing object check Preserve parameters in link headers (Mastodon API) [#647] tests for web push Keep heading short Add default config for masto_fe Add handling of objects not in database Add tests for reserved char escaping in upload Properly escape reserved URI charachters in upload urls Web.RelMe: Fix having other values in rel attr Plugs.HTTPSecurityPlug: Add static_url to CSP's connect-src Include admins in nodeinfo Typo rich media: helpers: rework validate_page_url() local -> only_local Format & update docs helpers: use AutoLinker to validate URIs as well as the other tests ...
This commit is contained in:
commit
66185ad957
71 changed files with 1860 additions and 464 deletions
12
TODO.txt
12
TODO.txt
|
|
@ -1,12 +0,0 @@
|
|||
Unliking:
|
||||
|
||||
- Add a proper undo activity, find out how to ignore those in twitter api.
|
||||
|
||||
WEBSUB:
|
||||
|
||||
- Add unsubscription
|
||||
|
||||
OSTATUS:
|
||||
|
||||
- Save and output 'updated'
|
||||
|
||||
|
|
@ -199,6 +199,9 @@ config :pleroma, :frontend_configurations,
|
|||
scopeCopy: false,
|
||||
subjectLineBehavior: "noop",
|
||||
alwaysShowSubjectInput: false
|
||||
},
|
||||
masto_fe: %{
|
||||
showInstanceSpecificPanel: true
|
||||
}
|
||||
|
||||
config :pleroma, :activitypub,
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ config :web_push_encryption, :vapid_details,
|
|||
"BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4",
|
||||
private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA"
|
||||
|
||||
config :web_push_encryption, :http_client, Pleroma.Web.WebPushHttpClientMock
|
||||
|
||||
config :pleroma, Pleroma.Jobs, testing: [max_jobs: 2]
|
||||
|
||||
try do
|
||||
|
|
|
|||
|
|
@ -7,17 +7,26 @@ Authentication is required and the user must be an admin.
|
|||
### List users
|
||||
|
||||
- Method `GET`
|
||||
- Query Params:
|
||||
- `query`: **string** *optional* search term
|
||||
- `local_only`: **bool** *optional* whether to return only local users
|
||||
- `page`: **integer** *optional* page number
|
||||
- `page_size`: **integer** *optional* number of users per page (default is `50`)
|
||||
- Response:
|
||||
|
||||
```JSON
|
||||
[
|
||||
{
|
||||
"page_size": integer,
|
||||
"count": integer,
|
||||
"users": [
|
||||
{
|
||||
"deactivated": bool,
|
||||
"id": integer,
|
||||
"nickname": string
|
||||
"deactivated": bool,
|
||||
"id": integer,
|
||||
"nickname": string
|
||||
},
|
||||
...
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## `/api/pleroma/admin/user`
|
||||
|
|
@ -49,9 +58,9 @@ Authentication is required and the user must be an admin.
|
|||
|
||||
```JSON
|
||||
{
|
||||
"deactivated": bool,
|
||||
"id": integer,
|
||||
"nickname": string
|
||||
"deactivated": bool,
|
||||
"id": integer,
|
||||
"nickname": string
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -81,8 +90,8 @@ Authentication is required and the user must be an admin.
|
|||
|
||||
```JSON
|
||||
{
|
||||
"is_moderator": bool,
|
||||
"is_admin": bool
|
||||
"is_moderator": bool,
|
||||
"is_admin": bool
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -98,8 +107,8 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
|||
|
||||
```JSON
|
||||
{
|
||||
"is_moderator": bool,
|
||||
"is_admin": bool
|
||||
"is_moderator": bool,
|
||||
"is_admin": bool
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ See: [logger’s documentation](https://hexdocs.pm/logger/Logger.html) and [ex_s
|
|||
|
||||
## :frontend_configurations
|
||||
|
||||
This can be used to configure a keyword list that keeps the configuration data for any kind of frontend. By default, settings for `pleroma_fe` are configured.
|
||||
This can be used to configure a keyword list that keeps the configuration data for any kind of frontend. By default, settings for `pleroma_fe` and `masto_fe` are configured.
|
||||
|
||||
Frontends can access these settings at `/api/pleroma/frontend_configurations`
|
||||
|
||||
|
|
|
|||
|
|
@ -34,13 +34,16 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do
|
|||
|
||||
defp csp_string do
|
||||
scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme]
|
||||
websocket_url = String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws")
|
||||
static_url = Pleroma.Web.Endpoint.static_url()
|
||||
websocket_url = String.replace(static_url, "http", "ws")
|
||||
|
||||
connect_src = "connect-src 'self' #{static_url} #{websocket_url}"
|
||||
|
||||
connect_src =
|
||||
if Mix.env() == :dev do
|
||||
"connect-src 'self' http://localhost:3035/ " <> websocket_url
|
||||
connect_src <> " http://localhost:3035/"
|
||||
else
|
||||
"connect-src 'self' " <> websocket_url
|
||||
connect_src
|
||||
end
|
||||
|
||||
script_src =
|
||||
|
|
|
|||
|
|
@ -85,6 +85,10 @@ defmodule Pleroma.Upload do
|
|||
end
|
||||
end
|
||||
|
||||
def char_unescaped?(char) do
|
||||
URI.char_unreserved?(char) or char == ?/
|
||||
end
|
||||
|
||||
defp get_opts(opts) do
|
||||
{size_limit, activity_type} =
|
||||
case Keyword.get(opts, :type) do
|
||||
|
|
@ -218,9 +222,7 @@ defmodule Pleroma.Upload do
|
|||
defp url_from_spec(base_url, {:file, path}) do
|
||||
path =
|
||||
path
|
||||
|> URI.encode()
|
||||
|> String.replace("?", "%3F")
|
||||
|> String.replace(":", "%3A")
|
||||
|> URI.encode(&char_unescaped?/1)
|
||||
|
||||
[base_url, "media", path]
|
||||
|> Path.join()
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ defmodule Pleroma.User do
|
|||
alias Pleroma.Web.OAuth
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.RelMe
|
||||
|
||||
require Logger
|
||||
|
||||
|
|
@ -547,11 +548,8 @@ defmodule Pleroma.User do
|
|||
end
|
||||
|
||||
def get_followers_query(user, page) do
|
||||
from(
|
||||
u in get_followers_query(user, nil),
|
||||
limit: 20,
|
||||
offset: ^((page - 1) * 20)
|
||||
)
|
||||
from(u in get_followers_query(user, nil))
|
||||
|> paginate(page, 20)
|
||||
end
|
||||
|
||||
def get_followers_query(user), do: get_followers_query(user, nil)
|
||||
|
|
@ -577,11 +575,8 @@ defmodule Pleroma.User do
|
|||
end
|
||||
|
||||
def get_friends_query(user, page) do
|
||||
from(
|
||||
u in get_friends_query(user, nil),
|
||||
limit: 20,
|
||||
offset: ^((page - 1) * 20)
|
||||
)
|
||||
from(u in get_friends_query(user, nil))
|
||||
|> paginate(page, 20)
|
||||
end
|
||||
|
||||
def get_friends_query(user), do: get_friends_query(user, nil)
|
||||
|
|
@ -621,64 +616,57 @@ defmodule Pleroma.User do
|
|||
)
|
||||
end
|
||||
|
||||
def update_follow_request_count(%User{} = user) do
|
||||
subquery =
|
||||
def get_follow_requests(%User{} = user) do
|
||||
users =
|
||||
user
|
||||
|> User.get_follow_requests_query()
|
||||
|> select([a], %{count: count(a.id)})
|
||||
|
||||
User
|
||||
|> where(id: ^user.id)
|
||||
|> join(:inner, [u], s in subquery(subquery))
|
||||
|> update([u, s],
|
||||
set: [
|
||||
info:
|
||||
fragment(
|
||||
"jsonb_set(?, '{follow_request_count}', ?::varchar::jsonb, true)",
|
||||
u.info,
|
||||
s.count
|
||||
)
|
||||
]
|
||||
)
|
||||
|> Repo.update_all([], returning: true)
|
||||
|> case do
|
||||
{1, [user]} -> {:ok, user}
|
||||
_ -> {:error, user}
|
||||
end
|
||||
end
|
||||
|
||||
def get_follow_requests(%User{} = user) do
|
||||
q = get_follow_requests_query(user)
|
||||
reqs = Repo.all(q)
|
||||
|
||||
users =
|
||||
Enum.map(reqs, fn req -> req.actor end)
|
||||
|> Enum.uniq()
|
||||
|> Enum.map(fn ap_id -> get_by_ap_id(ap_id) end)
|
||||
|> Enum.filter(fn u -> !is_nil(u) end)
|
||||
|> Enum.filter(fn u -> !following?(u, user) end)
|
||||
|> join(:inner, [a], u in User, a.actor == u.ap_id)
|
||||
|> where([a, u], not fragment("? @> ?", u.following, ^[user.follower_address]))
|
||||
|> group_by([a, u], u.id)
|
||||
|> select([a, u], u)
|
||||
|> Repo.all()
|
||||
|
||||
{:ok, users}
|
||||
end
|
||||
|
||||
def increase_note_count(%User{} = user) do
|
||||
info_cng = User.Info.add_to_note_count(user.info, 1)
|
||||
|
||||
cng =
|
||||
change(user)
|
||||
|> put_embed(:info, info_cng)
|
||||
|
||||
update_and_set_cache(cng)
|
||||
User
|
||||
|> where(id: ^user.id)
|
||||
|> update([u],
|
||||
set: [
|
||||
info:
|
||||
fragment(
|
||||
"jsonb_set(?, '{note_count}', ((?->>'note_count')::int + 1)::varchar::jsonb, true)",
|
||||
u.info,
|
||||
u.info
|
||||
)
|
||||
]
|
||||
)
|
||||
|> Repo.update_all([], returning: true)
|
||||
|> case do
|
||||
{1, [user]} -> set_cache(user)
|
||||
_ -> {:error, user}
|
||||
end
|
||||
end
|
||||
|
||||
def decrease_note_count(%User{} = user) do
|
||||
info_cng = User.Info.add_to_note_count(user.info, -1)
|
||||
|
||||
cng =
|
||||
change(user)
|
||||
|> put_embed(:info, info_cng)
|
||||
|
||||
update_and_set_cache(cng)
|
||||
User
|
||||
|> where(id: ^user.id)
|
||||
|> update([u],
|
||||
set: [
|
||||
info:
|
||||
fragment(
|
||||
"jsonb_set(?, '{note_count}', (greatest(0, (?->>'note_count')::int - 1))::varchar::jsonb, true)",
|
||||
u.info,
|
||||
u.info
|
||||
)
|
||||
]
|
||||
)
|
||||
|> Repo.update_all([], returning: true)
|
||||
|> case do
|
||||
{1, [user]} -> set_cache(user)
|
||||
_ -> {:error, user}
|
||||
end
|
||||
end
|
||||
|
||||
def update_note_count(%User{} = user) do
|
||||
|
|
@ -702,24 +690,29 @@ defmodule Pleroma.User do
|
|||
|
||||
def update_follower_count(%User{} = user) do
|
||||
follower_count_query =
|
||||
from(
|
||||
u in User,
|
||||
where: ^user.follower_address in u.following,
|
||||
where: u.id != ^user.id,
|
||||
select: count(u.id)
|
||||
)
|
||||
User
|
||||
|> where([u], ^user.follower_address in u.following)
|
||||
|> where([u], u.id != ^user.id)
|
||||
|> select([u], %{count: count(u.id)})
|
||||
|
||||
follower_count = Repo.one(follower_count_query)
|
||||
|
||||
info_cng =
|
||||
user.info
|
||||
|> User.Info.set_follower_count(follower_count)
|
||||
|
||||
cng =
|
||||
change(user)
|
||||
|> put_embed(:info, info_cng)
|
||||
|
||||
update_and_set_cache(cng)
|
||||
User
|
||||
|> where(id: ^user.id)
|
||||
|> join(:inner, [u], s in subquery(follower_count_query))
|
||||
|> update([u, s],
|
||||
set: [
|
||||
info:
|
||||
fragment(
|
||||
"jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)",
|
||||
u.info,
|
||||
s.count
|
||||
)
|
||||
]
|
||||
)
|
||||
|> Repo.update_all([], returning: true)
|
||||
|> case do
|
||||
{1, [user]} -> set_cache(user)
|
||||
_ -> {:error, user}
|
||||
end
|
||||
end
|
||||
|
||||
def get_users_from_set_query(ap_ids, false) do
|
||||
|
|
@ -756,6 +749,59 @@ defmodule Pleroma.User do
|
|||
Repo.all(query)
|
||||
end
|
||||
|
||||
@spec search_for_admin(%{
|
||||
local: boolean(),
|
||||
page: number(),
|
||||
page_size: number()
|
||||
}) :: {:ok, [Pleroma.User.t()], number()}
|
||||
def search_for_admin(%{query: nil, local: local, page: page, page_size: page_size}) do
|
||||
query =
|
||||
from(u in User, order_by: u.id)
|
||||
|> maybe_local_user_query(local)
|
||||
|
||||
paginated_query =
|
||||
query
|
||||
|> paginate(page, page_size)
|
||||
|
||||
count =
|
||||
query
|
||||
|> Repo.aggregate(:count, :id)
|
||||
|
||||
{:ok, Repo.all(paginated_query), count}
|
||||
end
|
||||
|
||||
@spec search_for_admin(%{
|
||||
query: binary(),
|
||||
admin: Pleroma.User.t(),
|
||||
local: boolean(),
|
||||
page: number(),
|
||||
page_size: number()
|
||||
}) :: {:ok, [Pleroma.User.t()], number()}
|
||||
def search_for_admin(%{
|
||||
query: term,
|
||||
admin: admin,
|
||||
local: local,
|
||||
page: page,
|
||||
page_size: page_size
|
||||
}) do
|
||||
term = String.trim_leading(term, "@")
|
||||
|
||||
local_paginated_query =
|
||||
User
|
||||
|> maybe_local_user_query(local)
|
||||
|> paginate(page, page_size)
|
||||
|
||||
search_query = fts_search_subquery(term, local_paginated_query)
|
||||
|
||||
count =
|
||||
term
|
||||
|> fts_search_subquery()
|
||||
|> maybe_local_user_query(local)
|
||||
|> Repo.aggregate(:count, :id)
|
||||
|
||||
{:ok, do_search(search_query, admin), count}
|
||||
end
|
||||
|
||||
def search(query, resolve \\ false, for_user \\ nil) do
|
||||
# Strip the beginning @ off if there is a query
|
||||
query = String.trim_leading(query, "@")
|
||||
|
|
@ -773,12 +819,6 @@ defmodule Pleroma.User do
|
|||
Enum.uniq_by(fts_results ++ trigram_results, & &1.id)
|
||||
end
|
||||
|
||||
def all_except_one(user) do
|
||||
query = from(u in User, where: u.id != ^user.id)
|
||||
|
||||
Repo.all(query)
|
||||
end
|
||||
|
||||
defp do_search(subquery, for_user, options \\ []) do
|
||||
q =
|
||||
from(
|
||||
|
|
@ -795,9 +835,9 @@ defmodule Pleroma.User do
|
|||
boost_search_results(results, for_user)
|
||||
end
|
||||
|
||||
defp fts_search_subquery(query) do
|
||||
defp fts_search_subquery(term, query \\ User) do
|
||||
processed_query =
|
||||
query
|
||||
term
|
||||
|> String.replace(~r/\W+/, " ")
|
||||
|> String.trim()
|
||||
|> String.split()
|
||||
|
|
@ -805,7 +845,7 @@ defmodule Pleroma.User do
|
|||
|> Enum.join(" | ")
|
||||
|
||||
from(
|
||||
u in User,
|
||||
u in query,
|
||||
select_merge: %{
|
||||
search_rank:
|
||||
fragment(
|
||||
|
|
@ -835,19 +875,19 @@ defmodule Pleroma.User do
|
|||
)
|
||||
end
|
||||
|
||||
defp trigram_search_subquery(query) do
|
||||
defp trigram_search_subquery(term) do
|
||||
from(
|
||||
u in User,
|
||||
select_merge: %{
|
||||
search_rank:
|
||||
fragment(
|
||||
"similarity(?, trim(? || ' ' || coalesce(?, '')))",
|
||||
^query,
|
||||
^term,
|
||||
u.nickname,
|
||||
u.name
|
||||
)
|
||||
},
|
||||
where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^query)
|
||||
where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^term)
|
||||
)
|
||||
end
|
||||
|
||||
|
|
@ -1005,9 +1045,13 @@ defmodule Pleroma.User do
|
|||
update_and_set_cache(cng)
|
||||
end
|
||||
|
||||
def local_user_query do
|
||||
def maybe_local_user_query(query, local) do
|
||||
if local, do: local_user_query(query), else: query
|
||||
end
|
||||
|
||||
def local_user_query(query \\ User) do
|
||||
from(
|
||||
u in User,
|
||||
u in query,
|
||||
where: u.local == true,
|
||||
where: not is_nil(u.nickname)
|
||||
)
|
||||
|
|
@ -1202,8 +1246,14 @@ defmodule Pleroma.User do
|
|||
{String.trim(name, ":"), url}
|
||||
end)
|
||||
|
||||
# TODO: get profile URLs other than user.ap_id
|
||||
profile_urls = [user.ap_id]
|
||||
|
||||
bio
|
||||
|> CommonUtils.format_input("text/plain", mentions_format: :full)
|
||||
|> CommonUtils.format_input("text/plain",
|
||||
mentions_format: :full,
|
||||
rel: &RelMe.maybe_put_rel_me(&1, profile_urls)
|
||||
)
|
||||
|> elem(0)
|
||||
|> Formatter.emojify(emoji)
|
||||
end
|
||||
|
|
@ -1299,4 +1349,11 @@ defmodule Pleroma.User do
|
|||
)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
defp paginate(query, page, page_size) do
|
||||
from(u in query,
|
||||
limit: ^page_size,
|
||||
offset: ^((page - 1) * page_size)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ defmodule Pleroma.User.Info do
|
|||
field(:source_data, :map, default: %{})
|
||||
field(:note_count, :integer, default: 0)
|
||||
field(:follower_count, :integer, default: 0)
|
||||
field(:follow_request_count, :integer, default: 0)
|
||||
field(:locked, :boolean, default: false)
|
||||
field(:confirmation_pending, :boolean, default: false)
|
||||
field(:confirmation_token, :string, default: nil)
|
||||
|
|
|
|||
|
|
@ -81,6 +81,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp check_remote_limit(_), do: true
|
||||
|
||||
def increase_note_count_if_public(actor, object) do
|
||||
if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
|
||||
end
|
||||
|
||||
def decrease_note_count_if_public(actor, object) do
|
||||
if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
|
||||
end
|
||||
|
||||
def insert(map, local \\ true) when is_map(map) do
|
||||
with nil <- Activity.normalize(map),
|
||||
map <- lazy_put_activity_defaults(map),
|
||||
|
|
@ -163,7 +171,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
),
|
||||
{:ok, activity} <- insert(create_data, local),
|
||||
# Changing note count prior to enqueuing federation task in order to avoid race conditions on updating user.info
|
||||
{:ok, _actor} <- User.increase_note_count(actor),
|
||||
{:ok, _actor} <- increase_note_count_if_public(actor, activity),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
end
|
||||
|
|
@ -175,8 +183,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
with data <- %{"to" => to, "type" => "Accept", "actor" => actor.ap_id, "object" => object},
|
||||
{:ok, activity} <- insert(data, local),
|
||||
:ok <- maybe_federate(activity),
|
||||
_ <- User.update_follow_request_count(actor) do
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
|
@ -187,8 +194,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
with data <- %{"to" => to, "type" => "Reject", "actor" => actor.ap_id, "object" => object},
|
||||
{:ok, activity} <- insert(data, local),
|
||||
:ok <- maybe_federate(activity),
|
||||
_ <- User.update_follow_request_count(actor) do
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
|
@ -286,8 +292,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
def follow(follower, followed, activity_id \\ nil, local \\ true) do
|
||||
with data <- make_follow_data(follower, followed, activity_id),
|
||||
{:ok, activity} <- insert(data, local),
|
||||
:ok <- maybe_federate(activity),
|
||||
_ <- User.update_follow_request_count(followed) do
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
|
@ -297,26 +302,26 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
{:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
|
||||
unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
|
||||
{:ok, activity} <- insert(unfollow_data, local),
|
||||
:ok <- maybe_federate(activity),
|
||||
_ <- User.update_follow_request_count(followed) do
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
||||
def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
|
||||
user = User.get_cached_by_ap_id(actor)
|
||||
to = object.data["to"] || [] ++ object.data["cc"] || []
|
||||
|
||||
data = %{
|
||||
"type" => "Delete",
|
||||
"actor" => actor,
|
||||
"object" => id,
|
||||
"to" => [user.follower_address, "https://www.w3.org/ns/activitystreams#Public"]
|
||||
"to" => to
|
||||
}
|
||||
|
||||
with {:ok, _} <- Object.delete(object),
|
||||
{:ok, activity} <- insert(data, local),
|
||||
# Changing note count prior to enqueuing federation task in order to avoid race conditions on updating user.info
|
||||
{:ok, _actor} <- User.decrease_note_count(user),
|
||||
{:ok, _actor} <- decrease_note_count_if_public(user, object),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
end
|
||||
|
|
@ -420,6 +425,30 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
@valid_visibilities ~w[direct unlisted public private]
|
||||
|
||||
defp restrict_visibility(query, %{visibility: visibility})
|
||||
when is_list(visibility) do
|
||||
if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
|
||||
query =
|
||||
from(
|
||||
a in query,
|
||||
where:
|
||||
fragment(
|
||||
"activity_visibility(?, ?, ?) = ANY (?)",
|
||||
a.actor,
|
||||
a.recipients,
|
||||
a.data,
|
||||
^visibility
|
||||
)
|
||||
)
|
||||
|
||||
Ecto.Adapters.SQL.to_sql(:all, Repo, query)
|
||||
|
||||
query
|
||||
else
|
||||
Logger.error("Could not restrict visibility to #{visibility}")
|
||||
end
|
||||
end
|
||||
|
||||
defp restrict_visibility(query, %{visibility: visibility})
|
||||
when visibility in @valid_visibilities do
|
||||
query =
|
||||
|
|
|
|||
|
|
@ -23,15 +23,21 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do
|
|||
defp score_displayname(_), do: 0.0
|
||||
|
||||
defp determine_if_followbot(%User{nickname: nickname, name: displayname}) do
|
||||
# nickname will always be a binary string because it's generated by Pleroma.
|
||||
nick_score =
|
||||
nickname
|
||||
|> String.downcase()
|
||||
|> score_nickname()
|
||||
|
||||
# displayname will either be a binary string or nil, if a displayname isn't set.
|
||||
name_score =
|
||||
displayname
|
||||
|> String.downcase()
|
||||
|> score_displayname()
|
||||
if is_binary(displayname) do
|
||||
displayname
|
||||
|> String.downcase()
|
||||
|> score_displayname()
|
||||
else
|
||||
0.0
|
||||
end
|
||||
|
||||
nick_score + name_score
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
alias Pleroma.Web
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Web.Router.Helpers
|
||||
|
|
@ -274,13 +275,31 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
Repo.all(query)
|
||||
end
|
||||
|
||||
def make_like_data(%User{ap_id: ap_id} = actor, %{data: %{"id" => id}} = object, activity_id) do
|
||||
def make_like_data(
|
||||
%User{ap_id: ap_id} = actor,
|
||||
%{data: %{"actor" => object_actor_id, "id" => id}} = object,
|
||||
activity_id
|
||||
) do
|
||||
object_actor = User.get_cached_by_ap_id(object_actor_id)
|
||||
|
||||
to =
|
||||
if Visibility.is_public?(object) do
|
||||
[actor.follower_address, object.data["actor"]]
|
||||
else
|
||||
[object.data["actor"]]
|
||||
end
|
||||
|
||||
cc =
|
||||
(object.data["to"] ++ (object.data["cc"] || []))
|
||||
|> List.delete(actor.ap_id)
|
||||
|> List.delete(object_actor.follower_address)
|
||||
|
||||
data = %{
|
||||
"type" => "Like",
|
||||
"actor" => ap_id,
|
||||
"object" => id,
|
||||
"to" => [actor.follower_address, object.data["actor"]],
|
||||
"cc" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"to" => to,
|
||||
"cc" => cc,
|
||||
"context" => object.data["context"]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||
@users_page_size 50
|
||||
|
||||
use Pleroma.Web, :controller
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Relay
|
||||
alias Pleroma.Web.TwitterAPI.UserView
|
||||
alias Pleroma.Web.MastodonAPI.Admin.AccountView
|
||||
|
||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||
|
||||
|
|
@ -48,7 +50,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
{:ok, updated_user} = User.deactivate(user, !user.info.deactivated)
|
||||
|
||||
conn
|
||||
|> json(UserView.render("show_for_admin.json", %{user: updated_user}))
|
||||
|> json(AccountView.render("show.json", %{user: updated_user}))
|
||||
end
|
||||
|
||||
def tag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
|
||||
|
|
@ -61,11 +63,26 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
do: json_response(conn, :no_content, "")
|
||||
end
|
||||
|
||||
def list_users(%{assigns: %{user: admin}} = conn, _data) do
|
||||
users = User.all_except_one(admin)
|
||||
def list_users(%{assigns: %{user: admin}} = conn, params) do
|
||||
{page, page_size} = page_params(params)
|
||||
|
||||
conn
|
||||
|> json(UserView.render("index_for_admin.json", %{users: users}))
|
||||
with {:ok, users, count} <-
|
||||
User.search_for_admin(%{
|
||||
query: params["query"],
|
||||
admin: admin,
|
||||
local: params["local_only"] == "true",
|
||||
page: page,
|
||||
page_size: page_size
|
||||
}),
|
||||
do:
|
||||
conn
|
||||
|> json(
|
||||
AccountView.render("index.json",
|
||||
users: users,
|
||||
count: count,
|
||||
page_size: page_size
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname})
|
||||
|
|
@ -211,4 +228,26 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
|> put_status(500)
|
||||
|> json("Something went wrong")
|
||||
end
|
||||
|
||||
defp page_params(params) do
|
||||
{get_page(params["page"]), get_page_size(params["page_size"])}
|
||||
end
|
||||
|
||||
defp get_page(page_string) when is_nil(page_string), do: 1
|
||||
|
||||
defp get_page(page_string) do
|
||||
case Integer.parse(page_string) do
|
||||
{page, _} -> page
|
||||
:error -> 1
|
||||
end
|
||||
end
|
||||
|
||||
defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
|
||||
|
||||
defp get_page_size(page_size_string) do
|
||||
case Integer.parse(page_size_string) do
|
||||
{page_size, _} -> page_size
|
||||
:error -> @users_page_size
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,6 +14,19 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
|
||||
import Pleroma.Web.CommonAPI.Utils
|
||||
|
||||
def follow(follower, followed) do
|
||||
with {:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
||||
{:ok, activity} <- ActivityPub.follow(follower, followed),
|
||||
{:ok, follower, followed} <-
|
||||
User.wait_and_refresh(
|
||||
Pleroma.Config.get([:activitypub, :follow_handshake_timeout]),
|
||||
follower,
|
||||
followed
|
||||
) do
|
||||
{:ok, follower, followed, activity}
|
||||
end
|
||||
end
|
||||
|
||||
def delete(activity_id, user) do
|
||||
with %Activity{data: %{"object" => %{"id" => object_id}}} <- Repo.get(Activity, activity_id),
|
||||
%Object{} = object <- Object.normalize(object_id),
|
||||
|
|
|
|||
|
|
@ -15,14 +15,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
alias Pleroma.Web
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MediaProxy
|
||||
alias Pleroma.Web.Push
|
||||
alias Push.Subscription
|
||||
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MastodonAPI.FilterView
|
||||
alias Pleroma.Web.MastodonAPI.ListView
|
||||
alias Pleroma.Web.MastodonAPI.MastodonView
|
||||
alias Pleroma.Web.MastodonAPI.PushSubscriptionView
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
alias Pleroma.Web.MastodonAPI.ReportView
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
|
@ -193,6 +190,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
|
||||
defp add_link_headers(conn, method, activities, param \\ nil, params \\ %{}) do
|
||||
params =
|
||||
conn.params
|
||||
|> Map.drop(["since_id", "max_id"])
|
||||
|> Map.merge(params)
|
||||
|
||||
last = List.last(activities)
|
||||
first = List.first(activities)
|
||||
|
||||
|
|
@ -292,13 +294,17 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
|
||||
def dm_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||
query =
|
||||
ActivityPub.fetch_activities_query(
|
||||
[user.ap_id],
|
||||
Map.merge(params, %{"type" => "Create", visibility: "direct"})
|
||||
)
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", "Create")
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("user", user)
|
||||
|> Map.put(:visibility, "direct")
|
||||
|
||||
activities = Repo.all(query)
|
||||
activities =
|
||||
[user.ap_id]
|
||||
|> ActivityPub.fetch_activities_query(params)
|
||||
|> Repo.all()
|
||||
|
||||
conn
|
||||
|> add_link_headers(:dm_timeline, activities)
|
||||
|
|
@ -733,14 +739,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
|
||||
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
||||
with %User{} = followed <- Repo.get(User, id),
|
||||
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
||||
{:ok, _activity} <- ActivityPub.follow(follower, followed),
|
||||
{:ok, follower, followed} <-
|
||||
User.wait_and_refresh(
|
||||
Config.get([:activitypub, :follow_handshake_timeout]),
|
||||
follower,
|
||||
followed
|
||||
) do
|
||||
{:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("relationship.json", %{user: follower, target: followed})
|
||||
|
|
@ -754,8 +753,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
|
||||
def follow(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
|
||||
with %User{} = followed <- Repo.get_by(User, nickname: uri),
|
||||
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
||||
{:ok, _activity} <- ActivityPub.follow(follower, followed) do
|
||||
{:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("account.json", %{user: followed, for: follower})
|
||||
|
|
@ -894,7 +892,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
|
||||
def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||
accounts = User.search(query, params["resolve"] == "true", user)
|
||||
accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user)
|
||||
|
||||
statuses = status_search(user, query)
|
||||
|
||||
|
|
@ -919,7 +917,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
|
||||
def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||
accounts = User.search(query, params["resolve"] == "true", user)
|
||||
accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user)
|
||||
|
||||
statuses = status_search(user, query)
|
||||
|
||||
|
|
@ -941,7 +939,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
|
||||
def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||
accounts = User.search(query, params["resolve"] == "true", user)
|
||||
accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user)
|
||||
|
||||
res = AccountView.render("accounts.json", users: accounts, for: user, as: :user)
|
||||
|
||||
|
|
@ -1424,37 +1422,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
json(conn, %{})
|
||||
end
|
||||
|
||||
def create_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||
true = Push.enabled()
|
||||
Subscription.delete_if_exists(user, token)
|
||||
{:ok, subscription} = Subscription.create(user, token, params)
|
||||
view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
|
||||
json(conn, view)
|
||||
end
|
||||
|
||||
def get_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||
true = Push.enabled()
|
||||
subscription = Subscription.get(user, token)
|
||||
view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
|
||||
json(conn, view)
|
||||
end
|
||||
|
||||
def update_push_subscription(
|
||||
%{assigns: %{user: user, token: token}} = conn,
|
||||
params
|
||||
) do
|
||||
true = Push.enabled()
|
||||
{:ok, subscription} = Subscription.update(user, token, params)
|
||||
view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
|
||||
json(conn, view)
|
||||
end
|
||||
|
||||
def delete_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||
true = Push.enabled()
|
||||
{:ok, _response} = Subscription.delete(user, token)
|
||||
json(conn, %{})
|
||||
end
|
||||
|
||||
# fallback action
|
||||
#
|
||||
def errors(conn, _) do
|
||||
conn
|
||||
|> put_status(500)
|
||||
|
|
|
|||
71
lib/pleroma/web/mastodon_api/subscription_controller.ex
Normal file
71
lib/pleroma/web/mastodon_api/subscription_controller.ex
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
|
||||
@moduledoc "The module represents functions to manage user subscriptions."
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Web.Push
|
||||
alias Pleroma.Web.Push.Subscription
|
||||
alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View
|
||||
|
||||
action_fallback(:errors)
|
||||
|
||||
# Creates PushSubscription
|
||||
# POST /api/v1/push/subscription
|
||||
#
|
||||
def create(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||
with true <- Push.enabled(),
|
||||
{:ok, _} <- Subscription.delete_if_exists(user, token),
|
||||
{:ok, subscription} <- Subscription.create(user, token, params) do
|
||||
view = View.render("push_subscription.json", subscription: subscription)
|
||||
json(conn, view)
|
||||
end
|
||||
end
|
||||
|
||||
# Gets PushSubscription
|
||||
# GET /api/v1/push/subscription
|
||||
#
|
||||
def get(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||
with true <- Push.enabled(),
|
||||
{:ok, subscription} <- Subscription.get(user, token) do
|
||||
view = View.render("push_subscription.json", subscription: subscription)
|
||||
json(conn, view)
|
||||
end
|
||||
end
|
||||
|
||||
# Updates PushSubscription
|
||||
# PUT /api/v1/push/subscription
|
||||
#
|
||||
def update(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||
with true <- Push.enabled(),
|
||||
{:ok, subscription} <- Subscription.update(user, token, params) do
|
||||
view = View.render("push_subscription.json", subscription: subscription)
|
||||
json(conn, view)
|
||||
end
|
||||
end
|
||||
|
||||
# Deletes PushSubscription
|
||||
# DELETE /api/v1/push/subscription
|
||||
#
|
||||
def delete(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||
with true <- Push.enabled(),
|
||||
{:ok, _response} <- Subscription.delete(user, token),
|
||||
do: json(conn, %{})
|
||||
end
|
||||
|
||||
# fallback action
|
||||
#
|
||||
def errors(conn, {:error, :not_found}) do
|
||||
conn
|
||||
|> put_status(404)
|
||||
|> json("Not found")
|
||||
end
|
||||
|
||||
def errors(conn, _) do
|
||||
conn
|
||||
|> put_status(500)
|
||||
|> json("Something went wrong")
|
||||
end
|
||||
end
|
||||
25
lib/pleroma/web/mastodon_api/views/admin/account_view.ex
Normal file
25
lib/pleroma/web/mastodon_api/views/admin/account_view.ex
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.Admin.AccountView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
alias Pleroma.Web.MastodonAPI.Admin.AccountView
|
||||
|
||||
def render("index.json", %{users: users, count: count, page_size: page_size}) do
|
||||
%{
|
||||
users: render_many(users, AccountView, "show.json", as: :user),
|
||||
count: count,
|
||||
page_size: page_size
|
||||
}
|
||||
end
|
||||
|
||||
def render("show.json", %{user: user}) do
|
||||
%{
|
||||
"id" => user.id,
|
||||
"nickname" => user.nickname,
|
||||
"deactivated" => user.info.deactivated
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do
|
||||
use Pleroma.Web, :view
|
||||
alias Pleroma.Web.Push
|
||||
|
||||
def render("push_subscription.json", %{subscription: subscription}) do
|
||||
%{
|
||||
|
|
@ -14,7 +15,5 @@ defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do
|
|||
}
|
||||
end
|
||||
|
||||
defp server_key do
|
||||
Keyword.get(Application.get_env(:web_push_encryption, :vapid_details), :public_key)
|
||||
end
|
||||
defp server_key, do: Keyword.get(Push.vapid_config(), :public_key)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,14 +17,14 @@ defmodule Pleroma.Web.Metadata.Utils do
|
|||
|> Formatter.truncate()
|
||||
end
|
||||
|
||||
def scrub_html_and_truncate(content) when is_binary(content) do
|
||||
def scrub_html_and_truncate(content, max_length \\ 200) when is_binary(content) do
|
||||
content
|
||||
# html content comes from DB already encoded, decode first and scrub after
|
||||
|> HtmlEntities.decode()
|
||||
|> String.replace(~r/<br\s?\/?>/, " ")
|
||||
|> HTML.strip_tags()
|
||||
|> Formatter.demojify()
|
||||
|> Formatter.truncate()
|
||||
|> Formatter.truncate(max_length)
|
||||
end
|
||||
|
||||
def attachment_url(url) do
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
|
|||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Stats
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web
|
||||
|
|
@ -86,8 +85,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
|
|||
end
|
||||
|
||||
staff_accounts =
|
||||
User.moderator_user_query()
|
||||
|> Repo.all()
|
||||
User.all_superusers()
|
||||
|> Enum.map(fn u -> u.ap_id end)
|
||||
|
||||
mrf_user_allowlist =
|
||||
|
|
|
|||
127
lib/pleroma/web/push/impl.ex
Normal file
127
lib/pleroma/web/push/impl.ex
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Push.Impl do
|
||||
@moduledoc "The module represents implementation push web notification"
|
||||
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.Push.Subscription
|
||||
alias Pleroma.Web.Metadata.Utils
|
||||
alias Pleroma.Notification
|
||||
|
||||
require Logger
|
||||
import Ecto.Query
|
||||
|
||||
@types ["Create", "Follow", "Announce", "Like"]
|
||||
|
||||
@doc "Performs sending notifications for user subscriptions"
|
||||
@spec perform_send(Notification.t()) :: list(any)
|
||||
def perform_send(%{activity: %{data: %{"type" => activity_type}}, user_id: user_id} = notif)
|
||||
when activity_type in @types do
|
||||
actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
|
||||
|
||||
type = Activity.mastodon_notification_type(notif.activity)
|
||||
gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key)
|
||||
avatar_url = User.avatar_url(actor)
|
||||
|
||||
for subscription <- fetch_subsriptions(user_id),
|
||||
get_in(subscription.data, ["alerts", type]) do
|
||||
%{
|
||||
title: format_title(notif),
|
||||
access_token: subscription.token.token,
|
||||
body: format_body(notif, actor),
|
||||
notification_id: notif.id,
|
||||
notification_type: type,
|
||||
icon: avatar_url,
|
||||
preferred_locale: "en"
|
||||
}
|
||||
|> Jason.encode!()
|
||||
|> push_message(build_sub(subscription), gcm_api_key, subscription)
|
||||
end
|
||||
end
|
||||
|
||||
def perform_send(_) do
|
||||
Logger.warn("Unknown notification type")
|
||||
:error
|
||||
end
|
||||
|
||||
@doc "Push message to web"
|
||||
def push_message(body, sub, api_key, subscription) do
|
||||
case WebPushEncryption.send_web_push(body, sub, api_key) do
|
||||
{:ok, %{status_code: code}} when 400 <= code and code < 500 ->
|
||||
Logger.debug("Removing subscription record")
|
||||
Repo.delete!(subscription)
|
||||
:ok
|
||||
|
||||
{:ok, %{status_code: code}} when 200 <= code and code < 300 ->
|
||||
:ok
|
||||
|
||||
{:ok, %{status_code: code}} ->
|
||||
Logger.error("Web Push Notification failed with code: #{code}")
|
||||
:error
|
||||
|
||||
_ ->
|
||||
Logger.error("Web Push Notification failed with unknown error")
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
@doc "Gets user subscriptions"
|
||||
def fetch_subsriptions(user_id) do
|
||||
Subscription
|
||||
|> where(user_id: ^user_id)
|
||||
|> preload(:token)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def build_sub(subscription) do
|
||||
%{
|
||||
keys: %{
|
||||
p256dh: subscription.key_p256dh,
|
||||
auth: subscription.key_auth
|
||||
},
|
||||
endpoint: subscription.endpoint
|
||||
}
|
||||
end
|
||||
|
||||
def format_body(
|
||||
%{activity: %{data: %{"type" => "Create", "object" => %{"content" => content}}}},
|
||||
actor
|
||||
) do
|
||||
"@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}"
|
||||
end
|
||||
|
||||
def format_body(
|
||||
%{activity: %{data: %{"type" => "Announce", "object" => activity_id}}},
|
||||
actor
|
||||
) do
|
||||
%Activity{data: %{"object" => %{"id" => object_id}}} = Activity.get_by_ap_id(activity_id)
|
||||
%Object{data: %{"content" => content}} = Object.get_by_ap_id(object_id)
|
||||
|
||||
"@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}"
|
||||
end
|
||||
|
||||
def format_body(
|
||||
%{activity: %{data: %{"type" => type}}},
|
||||
actor
|
||||
)
|
||||
when type in ["Follow", "Like"] do
|
||||
case type do
|
||||
"Follow" -> "@#{actor.nickname} has followed you"
|
||||
"Like" -> "@#{actor.nickname} has favorited your post"
|
||||
end
|
||||
end
|
||||
|
||||
def format_title(%{activity: %{data: %{"type" => type}}}) do
|
||||
case type do
|
||||
"Create" -> "New Mention"
|
||||
"Follow" -> "New Follower"
|
||||
"Announce" -> "New Repeat"
|
||||
"Like" -> "New Favorite"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,14 +5,13 @@
|
|||
defmodule Pleroma.Web.Push do
|
||||
use GenServer
|
||||
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Push.Subscription
|
||||
alias Pleroma.Web.Push.Impl
|
||||
|
||||
require Logger
|
||||
import Ecto.Query
|
||||
|
||||
@types ["Create", "Follow", "Announce", "Like"]
|
||||
##############
|
||||
# Client API #
|
||||
##############
|
||||
|
||||
def start_link() do
|
||||
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
|
||||
|
|
@ -30,14 +29,18 @@ defmodule Pleroma.Web.Push do
|
|||
end
|
||||
end
|
||||
|
||||
def send(notification) do
|
||||
if enabled() do
|
||||
GenServer.cast(Pleroma.Web.Push, {:send, notification})
|
||||
end
|
||||
end
|
||||
def send(notification),
|
||||
do: GenServer.cast(__MODULE__, {:send, notification})
|
||||
|
||||
####################
|
||||
# Server Callbacks #
|
||||
####################
|
||||
|
||||
@impl true
|
||||
def init(:ok) do
|
||||
if !enabled() do
|
||||
if enabled() do
|
||||
{:ok, nil}
|
||||
else
|
||||
Logger.warn("""
|
||||
VAPID key pair is not found. If you wish to enabled web push, please run
|
||||
|
||||
|
|
@ -47,93 +50,15 @@ defmodule Pleroma.Web.Push do
|
|||
""")
|
||||
|
||||
:ignore
|
||||
else
|
||||
{:ok, nil}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_cast(
|
||||
{:send, %{activity: %{data: %{"type" => type}}, user_id: user_id} = notification},
|
||||
state
|
||||
)
|
||||
when type in @types do
|
||||
actor = User.get_cached_by_ap_id(notification.activity.data["actor"])
|
||||
|
||||
type = Pleroma.Activity.mastodon_notification_type(notification.activity)
|
||||
|
||||
Subscription
|
||||
|> where(user_id: ^user_id)
|
||||
|> preload(:token)
|
||||
|> Repo.all()
|
||||
|> Enum.filter(fn subscription ->
|
||||
get_in(subscription.data, ["alerts", type]) || false
|
||||
end)
|
||||
|> Enum.each(fn subscription ->
|
||||
sub = %{
|
||||
keys: %{
|
||||
p256dh: subscription.key_p256dh,
|
||||
auth: subscription.key_auth
|
||||
},
|
||||
endpoint: subscription.endpoint
|
||||
}
|
||||
|
||||
body =
|
||||
Jason.encode!(%{
|
||||
title: format_title(notification),
|
||||
access_token: subscription.token.token,
|
||||
body: format_body(notification, actor),
|
||||
notification_id: notification.id,
|
||||
notification_type: type,
|
||||
icon: User.avatar_url(actor),
|
||||
preferred_locale: "en"
|
||||
})
|
||||
|
||||
case WebPushEncryption.send_web_push(
|
||||
body,
|
||||
sub,
|
||||
Application.get_env(:web_push_encryption, :gcm_api_key)
|
||||
) do
|
||||
{:ok, %{status_code: code}} when 400 <= code and code < 500 ->
|
||||
Logger.debug("Removing subscription record")
|
||||
Repo.delete!(subscription)
|
||||
:ok
|
||||
|
||||
{:ok, %{status_code: code}} when 200 <= code and code < 300 ->
|
||||
:ok
|
||||
|
||||
{:ok, %{status_code: code}} ->
|
||||
Logger.error("Web Push Notification failed with code: #{code}")
|
||||
:error
|
||||
|
||||
_ ->
|
||||
Logger.error("Web Push Notification failed with unknown error")
|
||||
:error
|
||||
end
|
||||
end)
|
||||
@impl true
|
||||
def handle_cast({:send, notification}, state) do
|
||||
if enabled() do
|
||||
Impl.perform_send(notification)
|
||||
end
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_cast({:send, _}, state) do
|
||||
Logger.warn("Unknown notification type")
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
defp format_title(%{activity: %{data: %{"type" => type}}}) do
|
||||
case type do
|
||||
"Create" -> "New Mention"
|
||||
"Follow" -> "New Follower"
|
||||
"Announce" -> "New Repeat"
|
||||
"Like" -> "New Favorite"
|
||||
end
|
||||
end
|
||||
|
||||
defp format_body(%{activity: %{data: %{"type" => type}}}, actor) do
|
||||
case type do
|
||||
"Create" -> "@#{actor.nickname} has mentioned you"
|
||||
"Follow" -> "@#{actor.nickname} has followed you"
|
||||
"Announce" -> "@#{actor.nickname} has repeated your post"
|
||||
"Like" -> "@#{actor.nickname} has favorited your post"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ defmodule Pleroma.Web.Push.Subscription do
|
|||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.Push.Subscription
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
schema "push_subscriptions" do
|
||||
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||
belongs_to(:token, Token)
|
||||
|
|
@ -50,24 +52,32 @@ defmodule Pleroma.Web.Push.Subscription do
|
|||
})
|
||||
end
|
||||
|
||||
@doc "Gets subsciption by user & token"
|
||||
@spec get(User.t(), Token.t()) :: {:ok, t()} | {:error, :not_found}
|
||||
def get(%User{id: user_id}, %Token{id: token_id}) do
|
||||
Repo.get_by(Subscription, user_id: user_id, token_id: token_id)
|
||||
case Repo.get_by(Subscription, user_id: user_id, token_id: token_id) do
|
||||
nil -> {:error, :not_found}
|
||||
subscription -> {:ok, subscription}
|
||||
end
|
||||
end
|
||||
|
||||
def update(user, token, params) do
|
||||
get(user, token)
|
||||
|> change(data: alerts(params))
|
||||
|> Repo.update()
|
||||
with {:ok, subscription} <- get(user, token) do
|
||||
subscription
|
||||
|> change(data: alerts(params))
|
||||
|> Repo.update()
|
||||
end
|
||||
end
|
||||
|
||||
def delete(user, token) do
|
||||
Repo.delete(get(user, token))
|
||||
with {:ok, subscription} <- get(user, token),
|
||||
do: Repo.delete(subscription)
|
||||
end
|
||||
|
||||
def delete_if_exists(user, token) do
|
||||
case get(user, token) do
|
||||
nil -> {:ok, nil}
|
||||
sub -> Repo.delete(sub)
|
||||
{:error, _} -> {:ok, nil}
|
||||
{:ok, sub} -> Repo.delete(sub)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
52
lib/pleroma/web/rel_me.ex
Normal file
52
lib/pleroma/web/rel_me.ex
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.RelMe do
|
||||
@hackney_options [
|
||||
pool: :media,
|
||||
timeout: 2_000,
|
||||
recv_timeout: 2_000,
|
||||
max_body: 2_000_000
|
||||
]
|
||||
|
||||
if Mix.env() == :test do
|
||||
def parse(url) when is_binary(url), do: parse_url(url)
|
||||
else
|
||||
def parse(url) when is_binary(url) do
|
||||
Cachex.fetch!(:rel_me_cache, url, fn _ ->
|
||||
{:commit, parse_url(url)}
|
||||
end)
|
||||
rescue
|
||||
e -> {:error, "Cachex error: #{inspect(e)}"}
|
||||
end
|
||||
end
|
||||
|
||||
def parse(_), do: {:error, "No URL provided"}
|
||||
|
||||
defp parse_url(url) do
|
||||
{:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: @hackney_options)
|
||||
|
||||
data =
|
||||
Floki.attribute(html, "link[rel~=me]", "href") ++
|
||||
Floki.attribute(html, "a[rel~=me]", "href")
|
||||
|
||||
{:ok, data}
|
||||
rescue
|
||||
e -> {:error, "Parsing error: #{inspect(e)}"}
|
||||
end
|
||||
|
||||
def maybe_put_rel_me("http" <> _ = target_page, profile_urls) when is_list(profile_urls) do
|
||||
{:ok, rel_me_hrefs} = parse(target_page)
|
||||
|
||||
true = Enum.any?(rel_me_hrefs, fn x -> x in profile_urls end)
|
||||
|
||||
"me"
|
||||
rescue
|
||||
_ -> nil
|
||||
end
|
||||
|
||||
def maybe_put_rel_me(_, _) do
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
|
@ -8,10 +8,24 @@ defmodule Pleroma.Web.RichMedia.Helpers do
|
|||
alias Pleroma.HTML
|
||||
alias Pleroma.Web.RichMedia.Parser
|
||||
|
||||
defp validate_page_url(page_url) when is_binary(page_url) do
|
||||
if AutoLinker.Parser.is_url?(page_url, true) do
|
||||
URI.parse(page_url) |> validate_page_url
|
||||
else
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_page_url(%URI{authority: nil}), do: :error
|
||||
defp validate_page_url(%URI{scheme: nil}), do: :error
|
||||
defp validate_page_url(%URI{}), do: :ok
|
||||
defp validate_page_url(_), do: :error
|
||||
|
||||
def fetch_data_for_activity(%Activity{} = activity) do
|
||||
with true <- Pleroma.Config.get([:rich_media, :enabled]),
|
||||
%Object{} = object <- Object.normalize(activity.data["object"]),
|
||||
{:ok, page_url} <- HTML.extract_first_external_url(object, object.data["content"]),
|
||||
:ok <- validate_page_url(page_url),
|
||||
{:ok, rich_media} <- Parser.parse(page_url) do
|
||||
%{page_url: page_url, rich_media: rich_media}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -303,10 +303,10 @@ defmodule Pleroma.Web.Router do
|
|||
scope [] do
|
||||
pipe_through(:oauth_push)
|
||||
|
||||
post("/push/subscription", MastodonAPIController, :create_push_subscription)
|
||||
get("/push/subscription", MastodonAPIController, :get_push_subscription)
|
||||
put("/push/subscription", MastodonAPIController, :update_push_subscription)
|
||||
delete("/push/subscription", MastodonAPIController, :delete_push_subscription)
|
||||
post("/push/subscription", SubscriptionController, :create)
|
||||
get("/push/subscription", SubscriptionController, :get)
|
||||
put("/push/subscription", SubscriptionController, :update)
|
||||
delete("/push/subscription", SubscriptionController, :delete)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -197,10 +197,12 @@ defmodule Pleroma.Web.Streamer do
|
|||
if socket.assigns[:user] do
|
||||
user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
|
||||
blocks = user.info.blocks || []
|
||||
mutes = user.info.mutes || []
|
||||
|
||||
parent = Object.normalize(item.data["object"])
|
||||
|
||||
unless is_nil(parent) or item.actor in blocks or parent.data["actor"] in blocks do
|
||||
unless is_nil(parent) or item.actor in blocks or item.actor in mutes or
|
||||
parent.data["actor"] in blocks or parent.data["actor"] in mutes do
|
||||
send(socket.transport_pid, {:text, represent_update(item, user)})
|
||||
end
|
||||
else
|
||||
|
|
@ -224,8 +226,9 @@ defmodule Pleroma.Web.Streamer do
|
|||
if socket.assigns[:user] do
|
||||
user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
|
||||
blocks = user.info.blocks || []
|
||||
mutes = user.info.mutes || []
|
||||
|
||||
unless item.actor in blocks do
|
||||
unless item.actor in blocks or item.actor in mutes do
|
||||
send(socket.transport_pid, {:text, represent_update(item, user)})
|
||||
end
|
||||
else
|
||||
|
|
|
|||
|
|
@ -28,18 +28,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
end
|
||||
|
||||
def follow(%User{} = follower, params) do
|
||||
with {:ok, %User{} = followed} <- get_user(params),
|
||||
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
||||
{:ok, activity} <- ActivityPub.follow(follower, followed),
|
||||
{:ok, follower, followed} <-
|
||||
User.wait_and_refresh(
|
||||
Pleroma.Config.get([:activitypub, :follow_handshake_timeout]),
|
||||
follower,
|
||||
followed
|
||||
) do
|
||||
{:ok, follower, followed, activity}
|
||||
else
|
||||
err -> err
|
||||
with {:ok, %User{} = followed} <- get_user(params) do
|
||||
CommonAPI.follow(follower, followed)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
params
|
||||
|> Map.put("type", ["Create", "Announce", "Follow", "Like"])
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put(:visibility, ~w[unlisted public private])
|
||||
|
||||
activities = ActivityPub.fetch_activities([user.ap_id], params)
|
||||
|
||||
|
|
@ -176,13 +177,16 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
end
|
||||
|
||||
def dm_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||
query =
|
||||
ActivityPub.fetch_activities_query(
|
||||
[user.ap_id],
|
||||
Map.merge(params, %{"type" => "Create", "user" => user, visibility: "direct"})
|
||||
)
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", "Create")
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("user", user)
|
||||
|> Map.put(:visibility, "direct")
|
||||
|
||||
activities = Repo.all(query)
|
||||
activities =
|
||||
ActivityPub.fetch_activities_query([user.ap_id], params)
|
||||
|> Repo.all()
|
||||
|
||||
conn
|
||||
|> put_view(ActivityView)
|
||||
|
|
@ -702,7 +706,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
end
|
||||
|
||||
def search_user(%{assigns: %{user: user}} = conn, %{"query" => query}) do
|
||||
users = User.search(query, true, user)
|
||||
users = User.search(query, resolve: true, for_user: user)
|
||||
|
||||
conn
|
||||
|> put_view(UserView)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI.Utils
|
||||
alias Pleroma.Web.MediaProxy
|
||||
alias Pleroma.Web.TwitterAPI.UserView
|
||||
|
||||
def render("show.json", %{user: user = %User{}} = assigns) do
|
||||
render_one(user, Pleroma.Web.TwitterAPI.UserView, "user.json", assigns)
|
||||
|
|
@ -27,19 +26,6 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
|
|||
else: %{}
|
||||
end
|
||||
|
||||
def render("index_for_admin.json", %{users: users} = opts) do
|
||||
users
|
||||
|> render_many(UserView, "show_for_admin.json", opts)
|
||||
end
|
||||
|
||||
def render("show_for_admin.json", %{user: user}) do
|
||||
%{
|
||||
"id" => user.id,
|
||||
"nickname" => user.nickname,
|
||||
"deactivated" => user.info.deactivated
|
||||
}
|
||||
end
|
||||
|
||||
def render("short.json", %{
|
||||
user: %User{
|
||||
nickname: nickname,
|
||||
|
|
@ -133,7 +119,6 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
|
|||
"tags" => user.tags
|
||||
}
|
||||
|> maybe_with_activation_status(user, for_user)
|
||||
|> maybe_with_follow_request_count(user, for_user)
|
||||
}
|
||||
|
||||
data =
|
||||
|
|
@ -155,14 +140,6 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
|
|||
|
||||
defp maybe_with_activation_status(data, _, _), do: data
|
||||
|
||||
defp maybe_with_follow_request_count(data, %User{id: id, info: %{locked: true}} = user, %User{
|
||||
id: id
|
||||
}) do
|
||||
Map.put(data, "follow_request_count", user.info.follow_request_count)
|
||||
end
|
||||
|
||||
defp maybe_with_follow_request_count(data, _, _), do: data
|
||||
|
||||
defp maybe_with_role(data, %User{id: id} = user, %User{id: id}) do
|
||||
Map.merge(data, %{"role" => role(user), "show_role" => user.info.show_role})
|
||||
end
|
||||
|
|
|
|||
2
mix.exs
2
mix.exs
|
|
@ -76,7 +76,7 @@ defmodule Pleroma.Mixfile do
|
|||
{:ex_aws, "~> 2.0"},
|
||||
{:ex_aws_s3, "~> 2.0"},
|
||||
{:earmark, "~> 1.3"},
|
||||
{:ex_machina, "~> 2.2", only: :test},
|
||||
{:ex_machina, "~> 2.3", only: :test},
|
||||
{:credo, "~> 0.9.3", only: [:dev, :test]},
|
||||
{:mock, "~> 0.3.1", only: :test},
|
||||
{:crypt,
|
||||
|
|
|
|||
8
mix.lock
8
mix.lock
|
|
@ -14,14 +14,14 @@
|
|||
"credo": {:hex, :credo, "0.9.3", "76fa3e9e497ab282e0cf64b98a624aa11da702854c52c82db1bf24e54ab7c97a", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"crypt": {:git, "https://github.com/msantos/crypt", "1f2b58927ab57e72910191a7ebaeff984382a1d3", [ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"]},
|
||||
"db_connection": {:hex, :db_connection, "1.1.3", "89b30ca1ef0a3b469b1c779579590688561d586694a3ce8792985d4d7e575a61", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"decimal": {:hex, :decimal, "1.6.0", "bfd84d90ff966e1f5d4370bdd3943432d8f65f07d3bab48001aebd7030590dcc", [:mix], [], "hexpm"},
|
||||
"decimal": {:hex, :decimal, "1.7.0", "30d6b52c88541f9a66637359ddf85016df9eb266170d53105f02e4a67e00c5aa", [:mix], [], "hexpm"},
|
||||
"earmark": {:hex, :earmark, "1.3.0", "17f0c38eaafb4800f746b457313af4b2442a8c2405b49c645768680f900be603", [:mix], [], "hexpm"},
|
||||
"ecto": {:hex, :ecto, "2.2.10", "e7366dc82f48f8dd78fcbf3ab50985ceeb11cb3dc93435147c6e13f2cda0992e", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ecto": {:hex, :ecto, "2.2.11", "4bb8f11718b72ba97a2696f65d247a379e739a0ecabf6a13ad1face79844791c", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"eternal": {:hex, :eternal, "1.2.0", "e2a6b6ce3b8c248f7dc31451aefca57e3bdf0e48d73ae5043229380a67614c41", [:mix], [], "hexpm"},
|
||||
"ex_aws": {:hex, :ex_aws, "2.1.0", "b92651527d6c09c479f9013caa9c7331f19cba38a650590d82ebf2c6c16a1d8a", [:mix], [{:configparser_ex, "~> 2.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "1.6.3 or 1.6.5 or 1.7.1 or 1.8.6 or ~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8", [hex: :jsx, repo: "hexpm", optional: true]}, {:poison, ">= 1.2.0", [hex: :poison, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}, {:xml_builder, "~> 0.1.0", [hex: :xml_builder, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ex_aws_s3": {:hex, :ex_aws_s3, "2.0.1", "9e09366e77f25d3d88c5393824e613344631be8db0d1839faca49686e99b6704", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.19.1", "519bb9c19526ca51d326c060cb1778d4a9056b190086a8c6c115828eaccea6cf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.7", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ex_machina": {:hex, :ex_machina, "2.2.0", "fec496331e04fc2db2a1a24fe317c12c0c4a50d2beb8ebb3531ed1f0d84be0ed", [:mix], [{:ecto, "~> 2.1", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ex_machina": {:hex, :ex_machina, "2.3.0", "92a5ad0a8b10ea6314b876a99c8c9e3f25f4dde71a2a835845b136b9adaf199a", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ex_syslogger": {:git, "https://github.com/slashmili/ex_syslogger.git", "f3963399047af17e038897c69e20d552e6899e1d", [tag: "1.4.0"]},
|
||||
"floki": {:hex, :floki, "0.20.4", "be42ac911fece24b4c72f3b5846774b6e61b83fe685c2fc9d62093277fb3bc86", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}, {:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"gen_smtp": {:hex, :gen_smtp, "0.13.0", "11f08504c4bdd831dc520b8f84a1dce5ce624474a797394e7aafd3c29f5dcd25", [:rebar3], [], "hexpm"},
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
"plug_cowboy": {:hex, :plug_cowboy, "2.0.1", "d798f8ee5acc86b7d42dbe4450b8b0dadf665ce588236eb0a751a132417a980e", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
|
||||
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
|
||||
"poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], [], "hexpm"},
|
||||
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm"},
|
||||
"postgrex": {:hex, :postgrex, "0.13.5", "3d931aba29363e1443da167a4b12f06dcd171103c424de15e5f3fc2ba3e6d9c5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
defmodule Pleroma.Repo.Migrations.UpdateUserNoteCounters do
|
||||
use Ecto.Migration
|
||||
|
||||
@public "https://www.w3.org/ns/activitystreams#Public"
|
||||
|
||||
def up do
|
||||
execute """
|
||||
WITH public_note_count AS (
|
||||
SELECT
|
||||
data->>'actor' AS actor,
|
||||
count(id) AS count
|
||||
FROM objects
|
||||
WHERE data->>'type' = 'Note' AND (
|
||||
data->'cc' ? '#{@public}' OR data->'to' ? '#{@public}'
|
||||
)
|
||||
GROUP BY data->>'actor'
|
||||
)
|
||||
UPDATE users AS u
|
||||
SET "info" = jsonb_set(u.info, '{note_count}', o.count::varchar::jsonb, true)
|
||||
FROM public_note_count AS o
|
||||
WHERE u.ap_id = o.actor
|
||||
"""
|
||||
end
|
||||
|
||||
def down do
|
||||
execute """
|
||||
WITH public_note_count AS (
|
||||
SELECT
|
||||
data->>'actor' AS actor,
|
||||
count(id) AS count
|
||||
FROM objects
|
||||
WHERE data->>'type' = 'Note'
|
||||
GROUP BY data->>'actor'
|
||||
)
|
||||
UPDATE users AS u
|
||||
SET "info" = jsonb_set(u.info, '{note_count}', o.count::varchar::jsonb, true)
|
||||
FROM public_note_count AS o
|
||||
WHERE u.ap_id = o.actor
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
|
@ -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.f8cd72107c472bb05fac426b53ec499b.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.7bce4ebd4510d2c5e6d8.js></script><script type=text/javascript src=/static/js/vendor.2a9228e5bcaf054e8060.js></script><script type=text/javascript src=/static/js/app.2a6f16b7ee4b2642dacc.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.6da3b5e56eb2330b1b175cca622a3d42.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.6c8fd5aa8c8c4aee99d3.js></script><script type=text/javascript src=/static/js/vendor.360be732615100da981f.js></script><script type=text/javascript src=/static/js/app.36ed651e315106252e56.js></script></body></html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
18
priv/static/static/js/app.36ed651e315106252e56.js
Normal file
18
priv/static/static/js/app.36ed651e315106252e56.js
Normal file
File diff suppressed because one or more lines are too long
1
priv/static/static/js/app.36ed651e315106252e56.js.map
Normal file
1
priv/static/static/js/app.36ed651e315106252e56.js.map
Normal file
File diff suppressed because one or more lines are too long
2
priv/static/static/js/manifest.6c8fd5aa8c8c4aee99d3.js
Normal file
2
priv/static/static/js/manifest.6c8fd5aa8c8c4aee99d3.js
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
!function(e){function t(r){if(n[r])return n[r].exports;var a=n[r]={exports:{},id:r,loaded:!1};return e[r].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var r=window.webpackJsonp;window.webpackJsonp=function(o,p){for(var c,l,s=0,i=[];s<o.length;s++)l=o[s],a[l]&&i.push.apply(i,a[l]),a[l]=0;for(c in p)Object.prototype.hasOwnProperty.call(p,c)&&(e[c]=p[c]);for(r&&r(o,p);i.length;)i.shift().call(null,t);if(p[0])return n[0]=0,t(0)};var n={},a={0:0};t.e=function(e,r){if(0===a[e])return r.call(null,t);if(void 0!==a[e])a[e].push(r);else{a[e]=[r];var n=document.getElementsByTagName("head")[0],o=document.createElement("script");o.type="text/javascript",o.charset="utf-8",o.async=!0,o.src=t.p+"static/js/"+e+"."+{1:"360be732615100da981f",2:"36ed651e315106252e56"}[e]+".js",n.appendChild(o)}},t.m=e,t.c=n,t.p="/"}([]);
|
||||
//# sourceMappingURL=manifest.6c8fd5aa8c8c4aee99d3.js.map
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,2 +0,0 @@
|
|||
!function(e){function t(a){if(r[a])return r[a].exports;var n=r[a]={exports:{},id:a,loaded:!1};return e[a].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var a=window.webpackJsonp;window.webpackJsonp=function(c,o){for(var p,l,s=0,i=[];s<c.length;s++)l=c[s],n[l]&&i.push.apply(i,n[l]),n[l]=0;for(p in o)Object.prototype.hasOwnProperty.call(o,p)&&(e[p]=o[p]);for(a&&a(c,o);i.length;)i.shift().call(null,t);if(o[0])return r[0]=0,t(0)};var r={},n={0:0};t.e=function(e,a){if(0===n[e])return a.call(null,t);if(void 0!==n[e])n[e].push(a);else{n[e]=[a];var r=document.getElementsByTagName("head")[0],c=document.createElement("script");c.type="text/javascript",c.charset="utf-8",c.async=!0,c.src=t.p+"static/js/"+e+"."+{1:"2a9228e5bcaf054e8060",2:"2a6f16b7ee4b2642dacc"}[e]+".js",r.appendChild(c)}},t.m=e,t.c=r,t.p="/"}([]);
|
||||
//# sourceMappingURL=manifest.7bce4ebd4510d2c5e6d8.js.map
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
46
priv/static/static/js/vendor.360be732615100da981f.js
Normal file
46
priv/static/static/js/vendor.360be732615100da981f.js
Normal file
File diff suppressed because one or more lines are too long
1
priv/static/static/js/vendor.360be732615100da981f.js.map
Normal file
1
priv/static/static/js/vendor.360be732615100da981f.js.map
Normal file
File diff suppressed because one or more lines are too long
10
priv/static/static/timeago-cs.json
Normal file
10
priv/static/static/timeago-cs.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[
|
||||
"teď",
|
||||
["%s s", "%s s"],
|
||||
["%s min", "%s min"],
|
||||
["%s h", "%s h"],
|
||||
["%s d", "%s d"],
|
||||
["%s týd", "%s týd"],
|
||||
["%s měs", "%s měs"],
|
||||
["%s r", "%s l"]
|
||||
]
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
var serviceWorkerOption = {"assets":["/static/img/nsfw.74818f9.png","/static/js/manifest.7bce4ebd4510d2c5e6d8.js","/static/js/vendor.2a9228e5bcaf054e8060.js","/static/js/app.2a6f16b7ee4b2642dacc.js","/static/css/app.f8cd72107c472bb05fac426b53ec499b.css"]};
|
||||
var serviceWorkerOption = {"assets":["/static/img/nsfw.74818f9.png","/static/js/manifest.6c8fd5aa8c8c4aee99d3.js","/static/js/vendor.360be732615100da981f.js","/static/js/app.36ed651e315106252e56.js","/static/css/app.6da3b5e56eb2330b1b175cca622a3d42.css"]};
|
||||
|
||||
!function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,n),o.loaded=!0,o.exports}var t={};return n.m=e,n.c=t,n.p="/",n(0)}([function(e,n,t){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(){return u.default.getItem("vuex-lz").then(function(e){return e.config.webPushNotifications})}function i(){return clients.matchAll({includeUncontrolled:!0}).then(function(e){return e.filter(function(e){var n=e.type;return"window"===n})})}var a=t(1),u=r(a);self.addEventListener("push",function(e){e.data&&e.waitUntil(o().then(function(n){return n&&i().then(function(n){var t=e.data.json();if(0===n.length)return self.registration.showNotification(t.title,t)})}))}),self.addEventListener("notificationclick",function(e){e.notification.close(),e.waitUntil(i().then(function(e){for(var n=0;n<e.length;n++){var t=e[n];if("/"===t.url&&"focus"in t)return t.focus()}if(clients.openWindow)return clients.openWindow("/")}))})},function(e,n){/*!
|
||||
localForage -- Offline Storage, Improved
|
||||
|
|
|
|||
14
test/fixtures/rel_me_anchor.html
vendored
Normal file
14
test/fixtures/rel_me_anchor.html
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Blog</title>
|
||||
</head>
|
||||
<body>
|
||||
<article>
|
||||
<h1>Lorem ipsum</h1>
|
||||
<p>Lorem ipsum dolor sit ameph, …</p>
|
||||
<a rel="me" href="https://social.example.org/users/lain">lain’s account</a>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
14
test/fixtures/rel_me_anchor_nofollow.html
vendored
Normal file
14
test/fixtures/rel_me_anchor_nofollow.html
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Blog</title>
|
||||
</head>
|
||||
<body>
|
||||
<article>
|
||||
<h1>Lorem ipsum</h1>
|
||||
<p>Lorem ipsum dolor sit ameph, …</p>
|
||||
<a rel="me nofollow" href="https://social.example.org/users/lain">lain’s account</a>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
14
test/fixtures/rel_me_link.html
vendored
Normal file
14
test/fixtures/rel_me_link.html
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Blog</title>
|
||||
<link rel="me" href="https://social.example.org/users/lain"/>
|
||||
</head>
|
||||
<body>
|
||||
<article>
|
||||
<h1>Lorem ipsum</h1>
|
||||
<p>Lorem ipsum dolor sit ameph, …</p>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
14
test/fixtures/rel_me_null.html
vendored
Normal file
14
test/fixtures/rel_me_null.html
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Blog</title>
|
||||
</head>
|
||||
<body>
|
||||
<article>
|
||||
<h1>Lorem ipsum</h1>
|
||||
<p>Lorem ipsum dolor sit ameph, …</p>
|
||||
<a rel="nofollow" href="https://social.example.org/users/lain">lain’s account</a>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -23,7 +23,7 @@ defmodule Pleroma.Factory do
|
|||
}
|
||||
end
|
||||
|
||||
def note_factory do
|
||||
def note_factory(attrs \\ %{}) do
|
||||
text = sequence(:text, &"This is :moominmamma: note #{&1}")
|
||||
|
||||
user = insert(:user)
|
||||
|
|
@ -46,7 +46,7 @@ defmodule Pleroma.Factory do
|
|||
}
|
||||
|
||||
%Pleroma.Object{
|
||||
data: data
|
||||
data: merge_attributes(data, Map.get(attrs, :data, %{}))
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -95,8 +95,8 @@ defmodule Pleroma.Factory do
|
|||
}
|
||||
end
|
||||
|
||||
def note_activity_factory do
|
||||
note = insert(:note)
|
||||
def note_activity_factory(attrs \\ %{}) do
|
||||
note = attrs[:note] || insert(:note)
|
||||
|
||||
data = %{
|
||||
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
|
||||
|
|
@ -135,9 +135,9 @@ defmodule Pleroma.Factory do
|
|||
}
|
||||
end
|
||||
|
||||
def announce_activity_factory do
|
||||
note_activity = insert(:note_activity)
|
||||
user = insert(:user)
|
||||
def announce_activity_factory(attrs \\ %{}) do
|
||||
note_activity = attrs[:note_activity] || insert(:note_activity)
|
||||
user = attrs[:user] || insert(:user)
|
||||
|
||||
data = %{
|
||||
"type" => "Announce",
|
||||
|
|
@ -229,15 +229,32 @@ defmodule Pleroma.Factory do
|
|||
end
|
||||
|
||||
def oauth_token_factory do
|
||||
user = insert(:user)
|
||||
oauth_app = insert(:oauth_app)
|
||||
|
||||
%Pleroma.Web.OAuth.Token{
|
||||
token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(),
|
||||
refresh_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(),
|
||||
user_id: user.id,
|
||||
user: build(:user),
|
||||
app_id: oauth_app.id,
|
||||
valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10)
|
||||
}
|
||||
end
|
||||
|
||||
def push_subscription_factory do
|
||||
%Pleroma.Web.Push.Subscription{
|
||||
user: build(:user),
|
||||
token: build(:oauth_token),
|
||||
endpoint: "https://example.com/example/1234",
|
||||
key_auth: "8eDyX_uCN0XRhSbY5hs7Hg==",
|
||||
key_p256dh:
|
||||
"BCIWgsnyXDv1VkhqL2P7YRBvdeuDnlwAPT2guNhdIoW3IP7GmHh1SMKPLxRf7x8vJy6ZFK3ol2ohgn_-0yP7QQA=",
|
||||
data: %{}
|
||||
}
|
||||
end
|
||||
|
||||
def notification_factory do
|
||||
%Pleroma.Notification{
|
||||
user: build(:user)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
23
test/support/web_push_http_client_mock.ex
Normal file
23
test/support/web_push_http_client_mock.ex
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.WebPushHttpClientMock do
|
||||
def get(url, headers \\ [], options \\ []) do
|
||||
{
|
||||
res,
|
||||
%Tesla.Env{status: status}
|
||||
} = Pleroma.HTTP.request(:get, url, "", headers, options)
|
||||
|
||||
{res, %{status_code: status}}
|
||||
end
|
||||
|
||||
def post(url, body, headers \\ [], options \\ []) do
|
||||
{
|
||||
res,
|
||||
%Tesla.Env{status: status}
|
||||
} = Pleroma.HTTP.request(:post, url, body, headers, options)
|
||||
|
||||
{res, %{status_code: status}}
|
||||
end
|
||||
end
|
||||
|
|
@ -153,19 +153,20 @@ defmodule Pleroma.UploadTest do
|
|||
assert Path.basename(attachment_url["href"]) == "an%E2%80%A6%20image.jpg"
|
||||
end
|
||||
|
||||
test "replaces : (colon) and ? (question-mark) to %3A and %3F (respectively)" do
|
||||
test "escapes reserved uri characters" do
|
||||
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
|
||||
|
||||
file = %Plug.Upload{
|
||||
content_type: "image/jpg",
|
||||
path: Path.absname("test/fixtures/image_tmp.jpg"),
|
||||
filename: "is:an?image.jpg"
|
||||
filename: ":?#[]@!$&\\'()*+,;=.jpg"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(file)
|
||||
[attachment_url | _] = data["url"]
|
||||
|
||||
assert Path.basename(attachment_url["href"]) == "is%3Aan%3Fimage.jpg"
|
||||
assert Path.basename(attachment_url["href"]) ==
|
||||
"%3A%3F%23%5B%5D%40%21%24%26%5C%27%28%29%2A%2B%2C%3B%3D.jpg"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -64,6 +64,20 @@ defmodule Pleroma.UserTest do
|
|||
assert activity
|
||||
end
|
||||
|
||||
test "doesn't return already accepted or duplicate follow requests" do
|
||||
locked = insert(:user, %{info: %{locked: true}})
|
||||
pending_follower = insert(:user)
|
||||
accepted_follower = insert(:user)
|
||||
|
||||
Pleroma.Web.TwitterAPI.TwitterAPI.follow(pending_follower, %{"user_id" => locked.id})
|
||||
Pleroma.Web.TwitterAPI.TwitterAPI.follow(pending_follower, %{"user_id" => locked.id})
|
||||
Pleroma.Web.TwitterAPI.TwitterAPI.follow(accepted_follower, %{"user_id" => locked.id})
|
||||
User.maybe_follow(accepted_follower, locked)
|
||||
|
||||
assert {:ok, [activity]} = User.get_follow_requests(locked)
|
||||
assert activity
|
||||
end
|
||||
|
||||
test "follow_all follows mutliple users" do
|
||||
user = insert(:user)
|
||||
followed_zero = insert(:user)
|
||||
|
|
@ -915,7 +929,8 @@ defmodule Pleroma.UserTest do
|
|||
{:ok, follower} = User.follow(follower, u1)
|
||||
{:ok, u1} = User.follow(u1, friend)
|
||||
|
||||
assert [friend.id, follower.id, u2.id] == Enum.map(User.search("doe", false, u1), & &1.id)
|
||||
assert [friend.id, follower.id, u2.id] --
|
||||
Enum.map(User.search("doe", resolve: false, for_user: u1), & &1.id) == []
|
||||
end
|
||||
|
||||
test "finds a user whose name is nil" do
|
||||
|
|
@ -937,7 +952,7 @@ defmodule Pleroma.UserTest do
|
|||
end
|
||||
|
||||
test "works with URIs" do
|
||||
results = User.search("http://mastodon.example.org/users/admin", true)
|
||||
results = User.search("http://mastodon.example.org/users/admin", resolve: true)
|
||||
result = results |> List.first()
|
||||
|
||||
user = User.get_by_ap_id("http://mastodon.example.org/users/admin")
|
||||
|
|
@ -1039,6 +1054,22 @@ defmodule Pleroma.UserTest do
|
|||
|
||||
assert expected_text == User.parse_bio(bio, user)
|
||||
end
|
||||
|
||||
test "Adds rel=me on linkbacked urls" do
|
||||
user = insert(:user, ap_id: "http://social.example.org/users/lain")
|
||||
|
||||
bio = "http://example.org/rel_me/null"
|
||||
expected_text = "<a href=\"#{bio}\">#{bio}</a>"
|
||||
assert expected_text == User.parse_bio(bio, user)
|
||||
|
||||
bio = "http://example.org/rel_me/link"
|
||||
expected_text = "<a href=\"#{bio}\">#{bio}</a>"
|
||||
assert expected_text == User.parse_bio(bio, user)
|
||||
|
||||
bio = "http://example.org/rel_me/anchor"
|
||||
expected_text = "<a href=\"#{bio}\">#{bio}</a>"
|
||||
assert expected_text == User.parse_bio(bio, user)
|
||||
end
|
||||
end
|
||||
|
||||
test "bookmarks" do
|
||||
|
|
|
|||
|
|
@ -55,6 +55,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
ActivityPub.fetch_activities([], %{:visibility => "public", "actor_id" => user.ap_id})
|
||||
|
||||
assert activities == [public_activity]
|
||||
|
||||
activities =
|
||||
ActivityPub.fetch_activities([], %{
|
||||
:visibility => ~w[private public],
|
||||
"actor_id" => user.ap_id
|
||||
})
|
||||
|
||||
assert activities == [public_activity, private_activity]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -205,6 +213,25 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
assert activity.actor == user.ap_id
|
||||
assert activity.recipients == ["user1", "user2", user.ap_id]
|
||||
end
|
||||
|
||||
test "increases user note count only for public activities" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, _} =
|
||||
CommonAPI.post(Repo.get(User, user.id), %{"status" => "1", "visibility" => "public"})
|
||||
|
||||
{:ok, _} =
|
||||
CommonAPI.post(Repo.get(User, user.id), %{"status" => "2", "visibility" => "unlisted"})
|
||||
|
||||
{:ok, _} =
|
||||
CommonAPI.post(Repo.get(User, user.id), %{"status" => "2", "visibility" => "private"})
|
||||
|
||||
{:ok, _} =
|
||||
CommonAPI.post(Repo.get(User, user.id), %{"status" => "3", "visibility" => "direct"})
|
||||
|
||||
user = Repo.get(User, user.id)
|
||||
assert user.info.note_count == 2
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetch activities for recipients" do
|
||||
|
|
@ -640,6 +667,51 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
|
||||
assert Repo.get(Object, object.id).data["type"] == "Tombstone"
|
||||
end
|
||||
|
||||
test "decrements user note count only for public activities" do
|
||||
user = insert(:user, info: %{note_count: 10})
|
||||
|
||||
{:ok, a1} =
|
||||
CommonAPI.post(Repo.get(User, user.id), %{"status" => "yeah", "visibility" => "public"})
|
||||
|
||||
{:ok, a2} =
|
||||
CommonAPI.post(Repo.get(User, user.id), %{"status" => "yeah", "visibility" => "unlisted"})
|
||||
|
||||
{:ok, a3} =
|
||||
CommonAPI.post(Repo.get(User, user.id), %{"status" => "yeah", "visibility" => "private"})
|
||||
|
||||
{:ok, a4} =
|
||||
CommonAPI.post(Repo.get(User, user.id), %{"status" => "yeah", "visibility" => "direct"})
|
||||
|
||||
{:ok, _} = a1.data["object"]["id"] |> Object.get_by_ap_id() |> ActivityPub.delete()
|
||||
{:ok, _} = a2.data["object"]["id"] |> Object.get_by_ap_id() |> ActivityPub.delete()
|
||||
{:ok, _} = a3.data["object"]["id"] |> Object.get_by_ap_id() |> ActivityPub.delete()
|
||||
{:ok, _} = a4.data["object"]["id"] |> Object.get_by_ap_id() |> ActivityPub.delete()
|
||||
|
||||
user = Repo.get(User, user.id)
|
||||
assert user.info.note_count == 10
|
||||
end
|
||||
|
||||
test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
|
||||
user = insert(:user)
|
||||
note = insert(:note_activity)
|
||||
|
||||
{:ok, object} =
|
||||
Object.get_by_ap_id(note.data["object"]["id"])
|
||||
|> Object.change(%{
|
||||
data: %{
|
||||
"actor" => note.data["object"]["actor"],
|
||||
"id" => note.data["object"]["id"],
|
||||
"to" => [user.ap_id],
|
||||
"type" => "Note"
|
||||
}
|
||||
})
|
||||
|> Object.update_and_set_cache()
|
||||
|
||||
{:ok, delete} = ActivityPub.delete(object)
|
||||
|
||||
assert user.ap_id in delete.data["to"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "timeline post-processing" do
|
||||
|
|
|
|||
|
|
@ -54,4 +54,19 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicyTest do
|
|||
|
||||
{:ok, _} = AntiFollowbotPolicy.filter(message)
|
||||
end
|
||||
|
||||
test "it gracefully handles nil display names" do
|
||||
actor = insert(:user, %{name: nil})
|
||||
target = insert(:user)
|
||||
|
||||
message = %{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Follow",
|
||||
"actor" => actor.ap_id,
|
||||
"object" => target.ap_id,
|
||||
"id" => "https://example.com/activities/1234"
|
||||
}
|
||||
|
||||
{:ok, _} = AntiFollowbotPolicy.filter(message)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
||||
use Pleroma.DataCase
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
describe "determine_explicit_mentions()" do
|
||||
test "works with an object that has mentions" do
|
||||
object = %{
|
||||
|
|
@ -54,4 +57,51 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
assert Utils.determine_explicit_mentions(object) == []
|
||||
end
|
||||
end
|
||||
|
||||
describe "make_like_data" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
third_user = insert(:user)
|
||||
[user: user, other_user: other_user, third_user: third_user]
|
||||
end
|
||||
|
||||
test "addresses actor's follower address if the activity is public", %{
|
||||
user: user,
|
||||
other_user: other_user,
|
||||
third_user: third_user
|
||||
} do
|
||||
expected_to = Enum.sort([user.ap_id, other_user.follower_address])
|
||||
expected_cc = Enum.sort(["https://www.w3.org/ns/activitystreams#Public", third_user.ap_id])
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
"status" =>
|
||||
"hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?"
|
||||
})
|
||||
|
||||
%{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
|
||||
assert Enum.sort(to) == expected_to
|
||||
assert Enum.sort(cc) == expected_cc
|
||||
end
|
||||
|
||||
test "does not adress actor's follower address if the activity is not public", %{
|
||||
user: user,
|
||||
other_user: other_user,
|
||||
third_user: third_user
|
||||
} do
|
||||
expected_to = Enum.sort([user.ap_id])
|
||||
expected_cc = [third_user.ap_id]
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
"status" => "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!",
|
||||
"visibility" => "private"
|
||||
})
|
||||
|
||||
%{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
|
||||
assert Enum.sort(to) == expected_to
|
||||
assert Enum.sort(cc) == expected_cc
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -331,22 +331,164 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
assert conn.status == 200
|
||||
end
|
||||
|
||||
test "GET /api/pleroma/admin/users" do
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
user = insert(:user)
|
||||
describe "GET /api/pleroma/admin/users" do
|
||||
test "renders users array for the first page" do
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
user = insert(:user)
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> get("/api/pleroma/admin/users")
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> get("/api/pleroma/admin/users?page=1")
|
||||
|
||||
assert json_response(conn, 200) == [
|
||||
%{
|
||||
"deactivated" => user.info.deactivated,
|
||||
"id" => user.id,
|
||||
"nickname" => user.nickname
|
||||
assert json_response(conn, 200) == %{
|
||||
"count" => 2,
|
||||
"page_size" => 50,
|
||||
"users" => [
|
||||
%{
|
||||
"deactivated" => admin.info.deactivated,
|
||||
"id" => admin.id,
|
||||
"nickname" => admin.nickname
|
||||
},
|
||||
%{
|
||||
"deactivated" => user.info.deactivated,
|
||||
"id" => user.id,
|
||||
"nickname" => user.nickname
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
test "renders empty array for the second page" do
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
insert(:user)
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> get("/api/pleroma/admin/users?page=2")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"count" => 2,
|
||||
"page_size" => 50,
|
||||
"users" => []
|
||||
}
|
||||
end
|
||||
|
||||
test "regular search" do
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
user = insert(:user, nickname: "bob")
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> get("/api/pleroma/admin/users?query=bo")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"count" => 1,
|
||||
"page_size" => 50,
|
||||
"users" => [
|
||||
%{
|
||||
"deactivated" => user.info.deactivated,
|
||||
"id" => user.id,
|
||||
"nickname" => user.nickname
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
test "regular search with page size" do
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
user = insert(:user, nickname: "bob")
|
||||
user2 = insert(:user, nickname: "bo")
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> get("/api/pleroma/admin/users?query=bo&page_size=1&page=1")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"count" => 2,
|
||||
"page_size" => 1,
|
||||
"users" => [
|
||||
%{
|
||||
"deactivated" => user.info.deactivated,
|
||||
"id" => user.id,
|
||||
"nickname" => user.nickname
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> get("/api/pleroma/admin/users?query=bo&page_size=1&page=2")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"count" => 2,
|
||||
"page_size" => 1,
|
||||
"users" => [
|
||||
%{
|
||||
"deactivated" => user2.info.deactivated,
|
||||
"id" => user2.id,
|
||||
"nickname" => user2.nickname
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
test "only local users" do
|
||||
admin = insert(:user, info: %{is_admin: true}, nickname: "john")
|
||||
user = insert(:user, nickname: "bob")
|
||||
|
||||
insert(:user, nickname: "bobb", local: false)
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> get("/api/pleroma/admin/users?query=bo&local_only=true")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"count" => 1,
|
||||
"page_size" => 50,
|
||||
"users" => [
|
||||
%{
|
||||
"deactivated" => user.info.deactivated,
|
||||
"id" => user.id,
|
||||
"nickname" => user.nickname
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
test "only local users with no query" do
|
||||
admin = insert(:user, info: %{is_admin: true}, nickname: "john")
|
||||
user = insert(:user, nickname: "bob")
|
||||
|
||||
insert(:user, nickname: "bobb", local: false)
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> get("/api/pleroma/admin/users?local_only=true")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"count" => 2,
|
||||
"page_size" => 50,
|
||||
"users" => [
|
||||
%{
|
||||
"deactivated" => admin.info.deactivated,
|
||||
"id" => admin.id,
|
||||
"nickname" => admin.nickname
|
||||
},
|
||||
%{
|
||||
"deactivated" => user.info.deactivated,
|
||||
"id" => user.id,
|
||||
"nickname" => user.nickname
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do
|
||||
|
|
|
|||
|
|
@ -248,6 +248,33 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
|||
assert status["url"] != direct.data["id"]
|
||||
end
|
||||
|
||||
test "doesn't include DMs from blocked users", %{conn: conn} do
|
||||
blocker = insert(:user)
|
||||
blocked = insert(:user)
|
||||
user = insert(:user)
|
||||
{:ok, blocker} = User.block(blocker, blocked)
|
||||
|
||||
{:ok, _blocked_direct} =
|
||||
CommonAPI.post(blocked, %{
|
||||
"status" => "Hi @#{blocker.nickname}!",
|
||||
"visibility" => "direct"
|
||||
})
|
||||
|
||||
{:ok, direct} =
|
||||
CommonAPI.post(user, %{
|
||||
"status" => "Hi @#{blocker.nickname}!",
|
||||
"visibility" => "direct"
|
||||
})
|
||||
|
||||
res_conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> get("api/v1/timelines/direct")
|
||||
|
||||
[status] = json_response(res_conn, 200)
|
||||
assert status["id"] == direct.id
|
||||
end
|
||||
|
||||
test "replying to a status", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
|
|
@ -946,7 +973,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
|||
other_user = Repo.get(User, other_user.id)
|
||||
|
||||
assert User.following?(other_user, user) == false
|
||||
assert user.info.follow_request_count == 1
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|
|
@ -960,7 +986,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
|||
other_user = Repo.get(User, other_user.id)
|
||||
|
||||
assert User.following?(other_user, user) == true
|
||||
assert user.info.follow_request_count == 0
|
||||
end
|
||||
|
||||
test "verify_credentials", %{conn: conn} do
|
||||
|
|
@ -982,7 +1007,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
|||
{:ok, _activity} = ActivityPub.follow(other_user, user)
|
||||
|
||||
user = Repo.get(User, user.id)
|
||||
assert user.info.follow_request_count == 1
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|
|
@ -996,7 +1020,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
|||
other_user = Repo.get(User, other_user.id)
|
||||
|
||||
assert User.following?(other_user, user) == false
|
||||
assert user.info.follow_request_count == 0
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -1932,4 +1955,36 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
|||
|> json_response(400)
|
||||
end
|
||||
end
|
||||
|
||||
describe "link headers" do
|
||||
test "preserves parameters in link headers", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity1} =
|
||||
CommonAPI.post(other_user, %{
|
||||
"status" => "hi @#{user.nickname}",
|
||||
"visibility" => "public"
|
||||
})
|
||||
|
||||
{:ok, activity2} =
|
||||
CommonAPI.post(other_user, %{
|
||||
"status" => "hi @#{user.nickname}",
|
||||
"visibility" => "public"
|
||||
})
|
||||
|
||||
notification1 = Repo.get_by(Notification, activity_id: activity1.id)
|
||||
notification2 = Repo.get_by(Notification, activity_id: activity2.id)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> get("/api/v1/notifications", %{media_only: true})
|
||||
|
||||
assert [link_header] = get_resp_header(conn, "link")
|
||||
assert link_header =~ ~r/media_only=true/
|
||||
assert link_header =~ ~r/since_id=#{notification2.id}/
|
||||
assert link_header =~ ~r/max_id=#{notification1.id}/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
23
test/web/mastodon_api/push_subscription_view_test.exs
Normal file
23
test/web/mastodon_api/push_subscription_view_test.exs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.PushSubscriptionViewTest do
|
||||
use Pleroma.DataCase
|
||||
import Pleroma.Factory
|
||||
alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View
|
||||
alias Pleroma.Web.Push
|
||||
|
||||
test "Represent a subscription" do
|
||||
subscription = insert(:push_subscription, data: %{"alerts" => %{"mention" => true}})
|
||||
|
||||
expected = %{
|
||||
alerts: %{"mention" => true},
|
||||
endpoint: subscription.endpoint,
|
||||
id: to_string(subscription.id),
|
||||
server_key: Keyword.get(Push.vapid_config(), :public_key)
|
||||
}
|
||||
|
||||
assert expected == View.render("push_subscription.json", %{subscription: subscription})
|
||||
end
|
||||
end
|
||||
192
test/web/mastodon_api/subscription_controller_test.exs
Normal file
192
test/web/mastodon_api/subscription_controller_test.exs
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
|
||||
import Pleroma.Factory
|
||||
alias Pleroma.Web.Push
|
||||
alias Pleroma.Web.Push.Subscription
|
||||
|
||||
@sub %{
|
||||
"endpoint" => "https://example.com/example/1234",
|
||||
"keys" => %{
|
||||
"auth" => "8eDyX_uCN0XRhSbY5hs7Hg==",
|
||||
"p256dh" =>
|
||||
"BCIWgsnyXDv1VkhqL2P7YRBvdeuDnlwAPT2guNhdIoW3IP7GmHh1SMKPLxRf7x8vJy6ZFK3ol2ohgn_-0yP7QQA="
|
||||
}
|
||||
}
|
||||
@server_key Keyword.get(Push.vapid_config(), :public_key)
|
||||
|
||||
setup do
|
||||
user = insert(:user)
|
||||
token = insert(:oauth_token, user: user, scopes: ["push"])
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, user)
|
||||
|> assign(:token, token)
|
||||
|
||||
%{conn: conn, user: user, token: token}
|
||||
end
|
||||
|
||||
defmacro assert_error_when_disable_push(do: yield) do
|
||||
quote do
|
||||
vapid_details = Application.get_env(:web_push_encryption, :vapid_details, [])
|
||||
Application.put_env(:web_push_encryption, :vapid_details, [])
|
||||
assert "Something went wrong" == unquote(yield)
|
||||
Application.put_env(:web_push_encryption, :vapid_details, vapid_details)
|
||||
end
|
||||
end
|
||||
|
||||
describe "creates push subscription" do
|
||||
test "returns error when push disabled ", %{conn: conn} do
|
||||
assert_error_when_disable_push do
|
||||
conn
|
||||
|> post("/api/v1/push/subscription", %{})
|
||||
|> json_response(500)
|
||||
end
|
||||
end
|
||||
|
||||
test "successful creation", %{conn: conn} do
|
||||
result =
|
||||
conn
|
||||
|> post("/api/v1/push/subscription", %{
|
||||
"data" => %{"alerts" => %{"mention" => true, "test" => true}},
|
||||
"subscription" => @sub
|
||||
})
|
||||
|> json_response(200)
|
||||
|
||||
[subscription] = Pleroma.Repo.all(Subscription)
|
||||
|
||||
assert %{
|
||||
"alerts" => %{"mention" => true},
|
||||
"endpoint" => subscription.endpoint,
|
||||
"id" => to_string(subscription.id),
|
||||
"server_key" => @server_key
|
||||
} == result
|
||||
end
|
||||
end
|
||||
|
||||
describe "gets a user subscription" do
|
||||
test "returns error when push disabled ", %{conn: conn} do
|
||||
assert_error_when_disable_push do
|
||||
conn
|
||||
|> get("/api/v1/push/subscription", %{})
|
||||
|> json_response(500)
|
||||
end
|
||||
end
|
||||
|
||||
test "returns error when user hasn't subscription", %{conn: conn} do
|
||||
res =
|
||||
conn
|
||||
|> get("/api/v1/push/subscription", %{})
|
||||
|> json_response(404)
|
||||
|
||||
assert "Not found" == res
|
||||
end
|
||||
|
||||
test "returns a user subsciption", %{conn: conn, user: user, token: token} do
|
||||
subscription =
|
||||
insert(:push_subscription,
|
||||
user: user,
|
||||
token: token,
|
||||
data: %{"alerts" => %{"mention" => true}}
|
||||
)
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get("/api/v1/push/subscription", %{})
|
||||
|> json_response(200)
|
||||
|
||||
expect = %{
|
||||
"alerts" => %{"mention" => true},
|
||||
"endpoint" => "https://example.com/example/1234",
|
||||
"id" => to_string(subscription.id),
|
||||
"server_key" => @server_key
|
||||
}
|
||||
|
||||
assert expect == res
|
||||
end
|
||||
end
|
||||
|
||||
describe "updates a user subsciption" do
|
||||
setup %{conn: conn, user: user, token: token} do
|
||||
subscription =
|
||||
insert(:push_subscription,
|
||||
user: user,
|
||||
token: token,
|
||||
data: %{"alerts" => %{"mention" => true}}
|
||||
)
|
||||
|
||||
%{conn: conn, user: user, token: token, subscription: subscription}
|
||||
end
|
||||
|
||||
test "returns error when push disabled ", %{conn: conn} do
|
||||
assert_error_when_disable_push do
|
||||
conn
|
||||
|> put("/api/v1/push/subscription", %{data: %{"alerts" => %{"mention" => false}}})
|
||||
|> json_response(500)
|
||||
end
|
||||
end
|
||||
|
||||
test "returns updated subsciption", %{conn: conn, subscription: subscription} do
|
||||
res =
|
||||
conn
|
||||
|> put("/api/v1/push/subscription", %{
|
||||
data: %{"alerts" => %{"mention" => false, "follow" => true}}
|
||||
})
|
||||
|> json_response(200)
|
||||
|
||||
expect = %{
|
||||
"alerts" => %{"follow" => true, "mention" => false},
|
||||
"endpoint" => "https://example.com/example/1234",
|
||||
"id" => to_string(subscription.id),
|
||||
"server_key" => @server_key
|
||||
}
|
||||
|
||||
assert expect == res
|
||||
end
|
||||
end
|
||||
|
||||
describe "deletes the user subscription" do
|
||||
test "returns error when push disabled ", %{conn: conn} do
|
||||
assert_error_when_disable_push do
|
||||
conn
|
||||
|> delete("/api/v1/push/subscription", %{})
|
||||
|> json_response(500)
|
||||
end
|
||||
end
|
||||
|
||||
test "returns error when user hasn't subscription", %{conn: conn} do
|
||||
res =
|
||||
conn
|
||||
|> delete("/api/v1/push/subscription", %{})
|
||||
|> json_response(404)
|
||||
|
||||
assert "Not found" == res
|
||||
end
|
||||
|
||||
test "returns empty result and delete user subsciption", %{
|
||||
conn: conn,
|
||||
user: user,
|
||||
token: token
|
||||
} do
|
||||
subscription =
|
||||
insert(:push_subscription,
|
||||
user: user,
|
||||
token: token,
|
||||
data: %{"alerts" => %{"mention" => true}}
|
||||
)
|
||||
|
||||
res =
|
||||
conn
|
||||
|> delete("/api/v1/push/subscription", %{})
|
||||
|> json_response(200)
|
||||
|
||||
assert %{} == res
|
||||
refute Pleroma.Repo.get(Subscription, subscription.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -8,7 +8,8 @@ defmodule Pleroma.Web.NodeInfoTest do
|
|||
import Pleroma.Factory
|
||||
|
||||
test "nodeinfo shows staff accounts", %{conn: conn} do
|
||||
user = insert(:user, %{local: true, info: %{is_moderator: true}})
|
||||
moderator = insert(:user, %{local: true, info: %{is_moderator: true}})
|
||||
admin = insert(:user, %{local: true, info: %{is_admin: true}})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|
|
@ -16,7 +17,8 @@ defmodule Pleroma.Web.NodeInfoTest do
|
|||
|
||||
assert result = json_response(conn, 200)
|
||||
|
||||
assert user.ap_id in result["metadata"]["staffAccounts"]
|
||||
assert moderator.ap_id in result["metadata"]["staffAccounts"]
|
||||
assert admin.ap_id in result["metadata"]["staffAccounts"]
|
||||
end
|
||||
|
||||
test "nodeinfo shows restricted nicknames", %{conn: conn} do
|
||||
|
|
|
|||
145
test/web/push/impl_test.exs
Normal file
145
test/web/push/impl_test.exs
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Push.ImplTest do
|
||||
use Pleroma.DataCase
|
||||
|
||||
alias Pleroma.Web.Push.Impl
|
||||
alias Pleroma.Web.Push.Subscription
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
setup_all do
|
||||
Tesla.Mock.mock_global(fn
|
||||
%{method: :post, url: "https://example.com/example/1234"} ->
|
||||
%Tesla.Env{status: 200}
|
||||
|
||||
%{method: :post, url: "https://example.com/example/not_found"} ->
|
||||
%Tesla.Env{status: 400}
|
||||
|
||||
%{method: :post, url: "https://example.com/example/bad"} ->
|
||||
%Tesla.Env{status: 100}
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
@sub %{
|
||||
endpoint: "https://example.com/example/1234",
|
||||
keys: %{
|
||||
auth: "8eDyX_uCN0XRhSbY5hs7Hg==",
|
||||
p256dh:
|
||||
"BCIWgsnyXDv1VkhqL2P7YRBvdeuDnlwAPT2guNhdIoW3IP7GmHh1SMKPLxRf7x8vJy6ZFK3ol2ohgn_-0yP7QQA="
|
||||
}
|
||||
}
|
||||
@api_key "BASgACIHpN1GYgzSRp"
|
||||
@message "@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
|
||||
|
||||
test "performs sending notifications" do
|
||||
user = insert(:user)
|
||||
user2 = insert(:user)
|
||||
insert(:push_subscription, user: user, data: %{alerts: %{"mention" => true}})
|
||||
insert(:push_subscription, user: user2, data: %{alerts: %{"mention" => true}})
|
||||
|
||||
insert(:push_subscription,
|
||||
user: user,
|
||||
data: %{alerts: %{"follow" => true, "mention" => true}}
|
||||
)
|
||||
|
||||
insert(:push_subscription,
|
||||
user: user,
|
||||
data: %{alerts: %{"follow" => true, "mention" => false}}
|
||||
)
|
||||
|
||||
notif =
|
||||
insert(:notification,
|
||||
user: user,
|
||||
activity: %Pleroma.Activity{
|
||||
data: %{
|
||||
"type" => "Create",
|
||||
"actor" => user.ap_id,
|
||||
"object" => %{"content" => "<Lorem ipsum dolor sit amet."}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
assert Impl.perform_send(notif) == [:ok, :ok]
|
||||
end
|
||||
|
||||
test "returns error if notif does not match " do
|
||||
assert Impl.perform_send(%{}) == :error
|
||||
end
|
||||
|
||||
test "successful message sending" do
|
||||
assert Impl.push_message(@message, @sub, @api_key, %Subscription{}) == :ok
|
||||
end
|
||||
|
||||
test "fail message sending" do
|
||||
assert Impl.push_message(
|
||||
@message,
|
||||
Map.merge(@sub, %{endpoint: "https://example.com/example/bad"}),
|
||||
@api_key,
|
||||
%Subscription{}
|
||||
) == :error
|
||||
end
|
||||
|
||||
test "delete subsciption if restult send message between 400..500" do
|
||||
subscription = insert(:push_subscription)
|
||||
|
||||
assert Impl.push_message(
|
||||
@message,
|
||||
Map.merge(@sub, %{endpoint: "https://example.com/example/not_found"}),
|
||||
@api_key,
|
||||
subscription
|
||||
) == :ok
|
||||
|
||||
refute Pleroma.Repo.get(Subscription, subscription.id)
|
||||
end
|
||||
|
||||
test "renders body for create activity" do
|
||||
assert Impl.format_body(
|
||||
%{
|
||||
activity: %{
|
||||
data: %{
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"content" =>
|
||||
"<span>Lorem ipsum dolor sit amet</span>, consectetur :bear: adipiscing elit. Fusce sagittis finibus turpis."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
%{nickname: "Bob"}
|
||||
) ==
|
||||
"@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
|
||||
end
|
||||
|
||||
test "renders body for follow activity" do
|
||||
assert Impl.format_body(%{activity: %{data: %{"type" => "Follow"}}}, %{nickname: "Bob"}) ==
|
||||
"@Bob has followed you"
|
||||
end
|
||||
|
||||
test "renders body for announce activity" do
|
||||
user = insert(:user)
|
||||
|
||||
note =
|
||||
insert(:note, %{
|
||||
data: %{
|
||||
"content" =>
|
||||
"<span>Lorem ipsum dolor sit amet</span>, consectetur :bear: adipiscing elit. Fusce sagittis finibus turpis."
|
||||
}
|
||||
})
|
||||
|
||||
note_activity = insert(:note_activity, %{note: note})
|
||||
announce_activity = insert(:announce_activity, %{user: user, note_activity: note_activity})
|
||||
|
||||
assert Impl.format_body(%{activity: announce_activity}, user) ==
|
||||
"@#{user.nickname} repeated: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
|
||||
end
|
||||
|
||||
test "renders body for like activity" do
|
||||
assert Impl.format_body(%{activity: %{data: %{"type" => "Like"}}}, %{nickname: "Bob"}) ==
|
||||
"@Bob has favorited your post"
|
||||
end
|
||||
end
|
||||
67
test/web/rel_me_test.exs
Normal file
67
test/web/rel_me_test.exs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
defmodule Pleroma.Web.RelMeTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
setup do
|
||||
Tesla.Mock.mock(fn
|
||||
%{
|
||||
method: :get,
|
||||
url: "http://example.com/rel_me/anchor"
|
||||
} ->
|
||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rel_me_anchor.html")}
|
||||
|
||||
%{
|
||||
method: :get,
|
||||
url: "http://example.com/rel_me/anchor_nofollow"
|
||||
} ->
|
||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rel_me_anchor_nofollow.html")}
|
||||
|
||||
%{
|
||||
method: :get,
|
||||
url: "http://example.com/rel_me/link"
|
||||
} ->
|
||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rel_me_link.html")}
|
||||
|
||||
%{
|
||||
method: :get,
|
||||
url: "http://example.com/rel_me/null"
|
||||
} ->
|
||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rel_me_null.html")}
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
test "parse/1" do
|
||||
hrefs = ["https://social.example.org/users/lain"]
|
||||
|
||||
assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/null") == {:ok, []}
|
||||
assert {:error, _} = Pleroma.Web.RelMe.parse("http://example.com/rel_me/error")
|
||||
|
||||
assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/link") == {:ok, hrefs}
|
||||
assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/anchor") == {:ok, hrefs}
|
||||
assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/anchor_nofollow") == {:ok, hrefs}
|
||||
end
|
||||
|
||||
test "maybe_put_rel_me/2" do
|
||||
profile_urls = ["https://social.example.org/users/lain"]
|
||||
attr = "me"
|
||||
fallback = nil
|
||||
|
||||
assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/null", profile_urls) ==
|
||||
fallback
|
||||
|
||||
assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/error", profile_urls) ==
|
||||
fallback
|
||||
|
||||
assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/anchor", profile_urls) ==
|
||||
attr
|
||||
|
||||
assert Pleroma.Web.RelMe.maybe_put_rel_me(
|
||||
"http://example.com/rel_me/anchor_nofollow",
|
||||
profile_urls
|
||||
) == attr
|
||||
|
||||
assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/link", profile_urls) ==
|
||||
attr
|
||||
end
|
||||
end
|
||||
62
test/web/rich_media/helpers_test.exs
Normal file
62
test/web/rich_media/helpers_test.exs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
defmodule Pleroma.Web.RichMedia.HelpersTest do
|
||||
use Pleroma.DataCase
|
||||
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
import Pleroma.Factory
|
||||
import Tesla.Mock
|
||||
|
||||
setup do
|
||||
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
:ok
|
||||
end
|
||||
|
||||
test "refuses to crawl incomplete URLs" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
"status" => "[test](example.com/ogp)",
|
||||
"content_type" => "text/markdown"
|
||||
})
|
||||
|
||||
Pleroma.Config.put([:rich_media, :enabled], true)
|
||||
|
||||
assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
|
||||
|
||||
Pleroma.Config.put([:rich_media, :enabled], false)
|
||||
end
|
||||
|
||||
test "refuses to crawl malformed URLs" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
"status" => "[test](example.com[]/ogp)",
|
||||
"content_type" => "text/markdown"
|
||||
})
|
||||
|
||||
Pleroma.Config.put([:rich_media, :enabled], true)
|
||||
|
||||
assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
|
||||
|
||||
Pleroma.Config.put([:rich_media, :enabled], false)
|
||||
end
|
||||
|
||||
test "crawls valid, complete URLs" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
"status" => "[test](http://example.com/ogp)",
|
||||
"content_type" => "text/markdown"
|
||||
})
|
||||
|
||||
Pleroma.Config.put([:rich_media, :enabled], true)
|
||||
|
||||
assert %{page_url: "http://example.com/ogp", rich_media: _} =
|
||||
Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
|
||||
|
||||
Pleroma.Config.put([:rich_media, :enabled], false)
|
||||
end
|
||||
end
|
||||
|
|
@ -415,6 +415,33 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|
|||
assert status["id"] == direct_two.id
|
||||
assert status_two["id"] == direct.id
|
||||
end
|
||||
|
||||
test "doesn't include DMs from blocked users", %{conn: conn} do
|
||||
blocker = insert(:user)
|
||||
blocked = insert(:user)
|
||||
user = insert(:user)
|
||||
{:ok, blocker} = User.block(blocker, blocked)
|
||||
|
||||
{:ok, _blocked_direct} =
|
||||
CommonAPI.post(blocked, %{
|
||||
"status" => "Hi @#{blocker.nickname}!",
|
||||
"visibility" => "direct"
|
||||
})
|
||||
|
||||
{:ok, direct} =
|
||||
CommonAPI.post(user, %{
|
||||
"status" => "Hi @#{blocker.nickname}!",
|
||||
"visibility" => "direct"
|
||||
})
|
||||
|
||||
res_conn =
|
||||
conn
|
||||
|> assign(:user, blocker)
|
||||
|> get("/api/statuses/dm_timeline.json")
|
||||
|
||||
[status] = json_response(res_conn, 200)
|
||||
assert status["id"] == direct.id
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /statuses/mentions.json" do
|
||||
|
|
@ -427,7 +454,10 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|
|||
|
||||
test "with credentials", %{conn: conn, user: current_user} do
|
||||
{:ok, activity} =
|
||||
ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: current_user})
|
||||
CommonAPI.post(current_user, %{
|
||||
"status" => "why is tenshi eating a corndog so cute?",
|
||||
"visibility" => "public"
|
||||
})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|
|
@ -445,6 +475,23 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|
|||
mentioned: [current_user]
|
||||
})
|
||||
end
|
||||
|
||||
test "does not show DMs in mentions timeline", %{conn: conn, user: current_user} do
|
||||
{:ok, _activity} =
|
||||
CommonAPI.post(current_user, %{
|
||||
"status" => "Have you guys ever seen how cute tenshi eating a corndog is?",
|
||||
"visibility" => "direct"
|
||||
})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> with_credentials(current_user.nickname, "test")
|
||||
|> get("/api/statuses/mentions.json")
|
||||
|
||||
response = json_response(conn, 200)
|
||||
|
||||
assert length(response) == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /api/qvitter/statuses/notifications.json" do
|
||||
|
|
@ -670,7 +717,6 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|
|||
followed = Repo.get(User, followed.id)
|
||||
|
||||
refute User.ap_followers(followed) in current_user.following
|
||||
assert followed.info.follow_request_count == 1
|
||||
|
||||
assert json_response(conn, 200) ==
|
||||
UserView.render("show.json", %{user: followed, for: current_user})
|
||||
|
|
@ -1737,19 +1783,15 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|
|||
other_user = Repo.get(User, other_user.id)
|
||||
|
||||
assert User.following?(other_user, user) == false
|
||||
assert user.info.follow_request_count == 1
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, user)
|
||||
|> post("/api/pleroma/friendships/approve", %{"user_id" => other_user.id})
|
||||
|
||||
user = Repo.get(User, user.id)
|
||||
|
||||
assert relationship = json_response(conn, 200)
|
||||
assert other_user.id == relationship["id"]
|
||||
assert relationship["follows_you"] == true
|
||||
assert user.info.follow_request_count == 0
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -1764,19 +1806,15 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|
|||
other_user = Repo.get(User, other_user.id)
|
||||
|
||||
assert User.following?(other_user, user) == false
|
||||
assert user.info.follow_request_count == 1
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, user)
|
||||
|> post("/api/pleroma/friendships/deny", %{"user_id" => other_user.id})
|
||||
|
||||
user = Repo.get(User, user.id)
|
||||
|
||||
assert relationship = json_response(conn, 200)
|
||||
assert other_user.id == relationship["id"]
|
||||
assert relationship["follows_you"] == false
|
||||
assert user.info.follow_request_count == 0
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue