forked from AkkomaGang/akkoma
Merge develop to 770-add-emoji-tags
Merge conflict in test/web/mastodon_api/mastodon_api_controller_test.exs
This commit is contained in:
commit
7410aee886
27 changed files with 498 additions and 70 deletions
|
@ -124,6 +124,11 @@
|
||||||
format: "$metadata[$level] $message",
|
format: "$metadata[$level] $message",
|
||||||
metadata: [:request_id]
|
metadata: [:request_id]
|
||||||
|
|
||||||
|
config :quack,
|
||||||
|
level: :warn,
|
||||||
|
meta: [:all],
|
||||||
|
webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
|
||||||
|
|
||||||
config :mime, :types, %{
|
config :mime, :types, %{
|
||||||
"application/xml" => ["xml"],
|
"application/xml" => ["xml"],
|
||||||
"application/xrd+xml" => ["xrd+xml"],
|
"application/xrd+xml" => ["xrd+xml"],
|
||||||
|
|
|
@ -58,6 +58,26 @@ Authentication is required and the user must be an admin.
|
||||||
- `password`
|
- `password`
|
||||||
- Response: User’s nickname
|
- Response: User’s nickname
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/user/follow`
|
||||||
|
### Make a user follow another user
|
||||||
|
|
||||||
|
- Methods: `POST`
|
||||||
|
- Params:
|
||||||
|
- `follower`: The nickname of the follower
|
||||||
|
- `followed`: The nickname of the followed
|
||||||
|
- Response:
|
||||||
|
- "ok"
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/user/unfollow`
|
||||||
|
### Make a user unfollow another user
|
||||||
|
|
||||||
|
- Methods: `POST`
|
||||||
|
- Params:
|
||||||
|
- `follower`: The nickname of the follower
|
||||||
|
- `followed`: The nickname of the followed
|
||||||
|
- Response:
|
||||||
|
- "ok"
|
||||||
|
|
||||||
## `/api/pleroma/admin/users/:nickname/toggle_activation`
|
## `/api/pleroma/admin/users/:nickname/toggle_activation`
|
||||||
|
|
||||||
### Toggle user activation
|
### Toggle user activation
|
||||||
|
|
|
@ -44,3 +44,9 @@ Has these additional fields under the `pleroma` object:
|
||||||
Has these additional fields under the `pleroma` object:
|
Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
- `is_seen`: true if the notification was read by the user
|
- `is_seen`: true if the notification was read by the user
|
||||||
|
|
||||||
|
## POST `/api/v1/statuses`
|
||||||
|
|
||||||
|
Additional parameters can be added to the JSON body/Form data:
|
||||||
|
|
||||||
|
- `preview`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example.
|
||||||
|
|
|
@ -105,7 +105,7 @@ config :pleroma, Pleroma.Mailer,
|
||||||
* `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). (Default: `false`)
|
* `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). (Default: `false`)
|
||||||
|
|
||||||
## :logger
|
## :logger
|
||||||
* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog
|
* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog, and `Quack.Logger` to log to Slack
|
||||||
|
|
||||||
An example to enable ONLY ExSyslogger (f/ex in ``prod.secret.exs``) with info and debug suppressed:
|
An example to enable ONLY ExSyslogger (f/ex in ``prod.secret.exs``) with info and debug suppressed:
|
||||||
```
|
```
|
||||||
|
@ -128,6 +128,24 @@ config :logger, :ex_syslogger,
|
||||||
|
|
||||||
See: [logger’s documentation](https://hexdocs.pm/logger/Logger.html) and [ex_syslogger’s documentation](https://hexdocs.pm/ex_syslogger/)
|
See: [logger’s documentation](https://hexdocs.pm/logger/Logger.html) and [ex_syslogger’s documentation](https://hexdocs.pm/ex_syslogger/)
|
||||||
|
|
||||||
|
An example of logging info to local syslog, but warn to a Slack channel:
|
||||||
|
```
|
||||||
|
config :logger,
|
||||||
|
backends: [ {ExSyslogger, :ex_syslogger}, Quack.Logger ],
|
||||||
|
level: :info
|
||||||
|
|
||||||
|
config :logger, :ex_syslogger,
|
||||||
|
level: :info,
|
||||||
|
ident: "pleroma",
|
||||||
|
format: "$metadata[$level] $message"
|
||||||
|
|
||||||
|
config :quack,
|
||||||
|
level: :warn,
|
||||||
|
meta: [:all],
|
||||||
|
webhook_url: "https://hooks.slack.com/services/YOUR-API-KEY-HERE"
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [Quack Github](https://github.com/azohra/quack) for more details
|
||||||
|
|
||||||
## :frontend_configurations
|
## :frontend_configurations
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,14 @@ def run(["gen" | rest]) do
|
||||||
|
|
||||||
email = Common.get_option(options, :admin_email, "What is your admin email address?")
|
email = Common.get_option(options, :admin_email, "What is your admin email address?")
|
||||||
|
|
||||||
|
indexable =
|
||||||
|
Common.get_option(
|
||||||
|
options,
|
||||||
|
:indexable,
|
||||||
|
"Do you want search engines to index your site? (y/n)",
|
||||||
|
"y"
|
||||||
|
) === "y"
|
||||||
|
|
||||||
dbhost =
|
dbhost =
|
||||||
Common.get_option(options, :dbhost, "What is the hostname of your database?", "localhost")
|
Common.get_option(options, :dbhost, "What is the hostname of your database?", "localhost")
|
||||||
|
|
||||||
|
@ -142,6 +150,8 @@ def run(["gen" | rest]) do
|
||||||
Mix.shell().info("Writing #{psql_path}.")
|
Mix.shell().info("Writing #{psql_path}.")
|
||||||
File.write(psql_path, result_psql)
|
File.write(psql_path, result_psql)
|
||||||
|
|
||||||
|
write_robots_txt(indexable)
|
||||||
|
|
||||||
Mix.shell().info(
|
Mix.shell().info(
|
||||||
"\n" <>
|
"\n" <>
|
||||||
"""
|
"""
|
||||||
|
@ -163,4 +173,28 @@ def run(["gen" | rest]) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp write_robots_txt(indexable) do
|
||||||
|
robots_txt =
|
||||||
|
EEx.eval_file(
|
||||||
|
Path.expand("robots_txt.eex", __DIR__),
|
||||||
|
indexable: indexable
|
||||||
|
)
|
||||||
|
|
||||||
|
static_dir = Pleroma.Config.get([:instance, :static_dir], "instance/static/")
|
||||||
|
|
||||||
|
unless File.exists?(static_dir) do
|
||||||
|
File.mkdir_p!(static_dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
robots_txt_path = Path.join(static_dir, "robots.txt")
|
||||||
|
|
||||||
|
if File.exists?(robots_txt_path) do
|
||||||
|
File.cp!(robots_txt_path, "#{robots_txt_path}.bak")
|
||||||
|
Mix.shell().info("Backing up existing robots.txt to #{robots_txt_path}.bak")
|
||||||
|
end
|
||||||
|
|
||||||
|
File.write(robots_txt_path, robots_txt)
|
||||||
|
Mix.shell().info("Writing #{robots_txt_path}.")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
2
lib/mix/tasks/pleroma/robots_txt.eex
Normal file
2
lib/mix/tasks/pleroma/robots_txt.eex
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
User-Agent: *
|
||||||
|
Disallow: <%= if indexable, do: "", else: "/" %>
|
|
@ -28,27 +28,39 @@ def filter_tags(html, scrubber), do: Scrubber.scrub(html, scrubber)
|
||||||
def filter_tags(html), do: filter_tags(html, nil)
|
def filter_tags(html), do: filter_tags(html, nil)
|
||||||
def strip_tags(html), do: Scrubber.scrub(html, Scrubber.StripTags)
|
def strip_tags(html), do: Scrubber.scrub(html, Scrubber.StripTags)
|
||||||
|
|
||||||
def get_cached_scrubbed_html_for_object(content, scrubbers, object, module) do
|
def get_cached_scrubbed_html_for_activity(content, scrubbers, activity, key \\ "") do
|
||||||
key = "#{module}#{generate_scrubber_signature(scrubbers)}|#{object.id}"
|
key = "#{key}#{generate_scrubber_signature(scrubbers)}|#{activity.id}"
|
||||||
Cachex.fetch!(:scrubber_cache, key, fn _key -> ensure_scrubbed_html(content, scrubbers) end)
|
|
||||||
|
Cachex.fetch!(:scrubber_cache, key, fn _key ->
|
||||||
|
ensure_scrubbed_html(content, scrubbers, activity.data["object"]["fake"] || false)
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_cached_stripped_html_for_object(content, object, module) do
|
def get_cached_stripped_html_for_activity(content, activity, key) do
|
||||||
get_cached_scrubbed_html_for_object(
|
get_cached_scrubbed_html_for_activity(
|
||||||
content,
|
content,
|
||||||
HtmlSanitizeEx.Scrubber.StripTags,
|
HtmlSanitizeEx.Scrubber.StripTags,
|
||||||
object,
|
activity,
|
||||||
module
|
key
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def ensure_scrubbed_html(
|
def ensure_scrubbed_html(
|
||||||
content,
|
content,
|
||||||
scrubbers
|
scrubbers,
|
||||||
|
false = _fake
|
||||||
) do
|
) do
|
||||||
{:commit, filter_tags(content, scrubbers)}
|
{:commit, filter_tags(content, scrubbers)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ensure_scrubbed_html(
|
||||||
|
content,
|
||||||
|
scrubbers,
|
||||||
|
true = _fake
|
||||||
|
) do
|
||||||
|
{:ignore, filter_tags(content, scrubbers)}
|
||||||
|
end
|
||||||
|
|
||||||
defp generate_scrubber_signature(scrubber) when is_atom(scrubber) do
|
defp generate_scrubber_signature(scrubber) when is_atom(scrubber) do
|
||||||
generate_scrubber_signature([scrubber])
|
generate_scrubber_signature([scrubber])
|
||||||
end
|
end
|
||||||
|
|
|
@ -44,6 +44,11 @@ def get_by_ap_id(ap_id) do
|
||||||
# Use this whenever possible, especially when walking graphs in an O(N) loop!
|
# Use this whenever possible, especially when walking graphs in an O(N) loop!
|
||||||
def normalize(%Activity{object: %Object{} = object}), do: object
|
def normalize(%Activity{object: %Object{} = object}), do: object
|
||||||
|
|
||||||
|
# A hack for fake activities
|
||||||
|
def normalize(%Activity{data: %{"object" => %{"fake" => true} = data}}) do
|
||||||
|
%Object{id: "pleroma:fake_object_id", data: data}
|
||||||
|
end
|
||||||
|
|
||||||
# Catch and log Object.normalize() calls where the Activity's child object is not
|
# Catch and log Object.normalize() calls where the Activity's child object is not
|
||||||
# preloaded.
|
# preloaded.
|
||||||
def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}) do
|
def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}) do
|
||||||
|
|
|
@ -113,15 +113,15 @@ def decrease_replies_count_if_reply(%Object{
|
||||||
|
|
||||||
def decrease_replies_count_if_reply(_object), do: :noop
|
def decrease_replies_count_if_reply(_object), do: :noop
|
||||||
|
|
||||||
def insert(map, local \\ true) when is_map(map) do
|
def insert(map, local \\ true, fake \\ false) when is_map(map) do
|
||||||
with nil <- Activity.normalize(map),
|
with nil <- Activity.normalize(map),
|
||||||
map <- lazy_put_activity_defaults(map),
|
map <- lazy_put_activity_defaults(map, fake),
|
||||||
:ok <- check_actor_is_active(map["actor"]),
|
:ok <- check_actor_is_active(map["actor"]),
|
||||||
{_, true} <- {:remote_limit_error, check_remote_limit(map)},
|
{_, true} <- {:remote_limit_error, check_remote_limit(map)},
|
||||||
{:ok, map} <- MRF.filter(map),
|
{:ok, map} <- MRF.filter(map),
|
||||||
|
{recipients, _, _} = get_recipients(map),
|
||||||
|
{:fake, false, map, recipients} <- {:fake, fake, map, recipients},
|
||||||
{:ok, object} <- insert_full_object(map) do
|
{:ok, object} <- insert_full_object(map) do
|
||||||
{recipients, _, _} = get_recipients(map)
|
|
||||||
|
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
Repo.insert(%Activity{
|
Repo.insert(%Activity{
|
||||||
data: map,
|
data: map,
|
||||||
|
@ -146,8 +146,23 @@ def insert(map, local \\ true) when is_map(map) do
|
||||||
stream_out(activity)
|
stream_out(activity)
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
%Activity{} = activity -> {:ok, activity}
|
%Activity{} = activity ->
|
||||||
error -> {:error, error}
|
{:ok, activity}
|
||||||
|
|
||||||
|
{:fake, true, map, recipients} ->
|
||||||
|
activity = %Activity{
|
||||||
|
data: map,
|
||||||
|
local: local,
|
||||||
|
actor: map["actor"],
|
||||||
|
recipients: recipients,
|
||||||
|
id: "pleroma:fakeid"
|
||||||
|
}
|
||||||
|
|
||||||
|
Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
|
||||||
|
{:ok, activity}
|
||||||
|
|
||||||
|
error ->
|
||||||
|
{:error, error}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -190,7 +205,7 @@ def stream_out(activity) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(%{to: to, actor: actor, context: context, object: object} = params) do
|
def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do
|
||||||
additional = params[:additional] || %{}
|
additional = params[:additional] || %{}
|
||||||
# only accept false as false value
|
# only accept false as false value
|
||||||
local = !(params[:local] == false)
|
local = !(params[:local] == false)
|
||||||
|
@ -201,13 +216,17 @@ def create(%{to: to, actor: actor, context: context, object: object} = params) d
|
||||||
%{to: to, actor: actor, published: published, context: context, object: object},
|
%{to: to, actor: actor, published: published, context: context, object: object},
|
||||||
additional
|
additional
|
||||||
),
|
),
|
||||||
{:ok, activity} <- insert(create_data, local),
|
{:ok, activity} <- insert(create_data, local, fake),
|
||||||
|
{:fake, false, activity} <- {:fake, fake, activity},
|
||||||
_ <- increase_replies_count_if_reply(create_data),
|
_ <- increase_replies_count_if_reply(create_data),
|
||||||
# Changing note count prior to enqueuing federation task in order to avoid
|
# Changing note count prior to enqueuing federation task in order to avoid
|
||||||
# race conditions on updating user.info
|
# race conditions on updating user.info
|
||||||
{:ok, _actor} <- increase_note_count_if_public(actor, activity),
|
{:ok, _actor} <- increase_note_count_if_public(actor, activity),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
{:fake, true, activity} ->
|
||||||
|
{:ok, activity}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -175,18 +175,26 @@ def maybe_federate(_), do: :ok
|
||||||
Adds an id and a published data if they aren't there,
|
Adds an id and a published data if they aren't there,
|
||||||
also adds it to an included object
|
also adds it to an included object
|
||||||
"""
|
"""
|
||||||
def lazy_put_activity_defaults(map) do
|
def lazy_put_activity_defaults(map, fake \\ false) do
|
||||||
%{data: %{"id" => context}, id: context_id} = create_context(map["context"])
|
|
||||||
|
|
||||||
map =
|
map =
|
||||||
map
|
unless fake do
|
||||||
|> Map.put_new_lazy("id", &generate_activity_id/0)
|
%{data: %{"id" => context}, id: context_id} = create_context(map["context"])
|
||||||
|> Map.put_new_lazy("published", &make_date/0)
|
|
||||||
|> Map.put_new("context", context)
|
map
|
||||||
|> Map.put_new("context_id", context_id)
|
|> Map.put_new_lazy("id", &generate_activity_id/0)
|
||||||
|
|> Map.put_new_lazy("published", &make_date/0)
|
||||||
|
|> Map.put_new("context", context)
|
||||||
|
|> Map.put_new("context_id", context_id)
|
||||||
|
else
|
||||||
|
map
|
||||||
|
|> Map.put_new("id", "pleroma:fakeid")
|
||||||
|
|> Map.put_new_lazy("published", &make_date/0)
|
||||||
|
|> Map.put_new("context", "pleroma:fakecontext")
|
||||||
|
|> Map.put_new("context_id", -1)
|
||||||
|
end
|
||||||
|
|
||||||
if is_map(map["object"]) do
|
if is_map(map["object"]) do
|
||||||
object = lazy_put_object_defaults(map["object"], map)
|
object = lazy_put_object_defaults(map["object"], map, fake)
|
||||||
%{map | "object" => object}
|
%{map | "object" => object}
|
||||||
else
|
else
|
||||||
map
|
map
|
||||||
|
@ -196,7 +204,18 @@ def lazy_put_activity_defaults(map) do
|
||||||
@doc """
|
@doc """
|
||||||
Adds an id and published date if they aren't there.
|
Adds an id and published date if they aren't there.
|
||||||
"""
|
"""
|
||||||
def lazy_put_object_defaults(map, activity \\ %{}) do
|
def lazy_put_object_defaults(map, activity \\ %{}, fake)
|
||||||
|
|
||||||
|
def lazy_put_object_defaults(map, activity, true = _fake) do
|
||||||
|
map
|
||||||
|
|> Map.put_new_lazy("published", &make_date/0)
|
||||||
|
|> Map.put_new("id", "pleroma:fake_object_id")
|
||||||
|
|> Map.put_new("context", activity["context"])
|
||||||
|
|> Map.put_new("fake", true)
|
||||||
|
|> Map.put_new("context_id", activity["context_id"])
|
||||||
|
end
|
||||||
|
|
||||||
|
def lazy_put_object_defaults(map, activity, _fake) do
|
||||||
map
|
map
|
||||||
|> Map.put_new_lazy("id", &generate_object_id/0)
|
|> Map.put_new_lazy("id", &generate_object_id/0)
|
||||||
|> Map.put_new_lazy("published", &make_date/0)
|
|> Map.put_new_lazy("published", &make_date/0)
|
||||||
|
@ -404,13 +423,15 @@ def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do
|
||||||
activity.data
|
activity.data
|
||||||
),
|
),
|
||||||
where: activity.actor == ^follower_id,
|
where: activity.actor == ^follower_id,
|
||||||
|
# this is to use the index
|
||||||
where:
|
where:
|
||||||
fragment(
|
fragment(
|
||||||
"? @> ?",
|
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||||
activity.data,
|
activity.data,
|
||||||
^%{object: followed_id}
|
activity.data,
|
||||||
|
^followed_id
|
||||||
),
|
),
|
||||||
order_by: [desc: :id],
|
order_by: [fragment("? desc nulls last", activity.id)],
|
||||||
limit: 1
|
limit: 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -567,13 +588,15 @@ def fetch_latest_block(%User{ap_id: blocker_id}, %User{ap_id: blocked_id}) do
|
||||||
activity.data
|
activity.data
|
||||||
),
|
),
|
||||||
where: activity.actor == ^blocker_id,
|
where: activity.actor == ^blocker_id,
|
||||||
|
# this is to use the index
|
||||||
where:
|
where:
|
||||||
fragment(
|
fragment(
|
||||||
"? @> ?",
|
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||||
activity.data,
|
activity.data,
|
||||||
^%{object: blocked_id}
|
activity.data,
|
||||||
|
^blocked_id
|
||||||
),
|
),
|
||||||
order_by: [desc: :id],
|
order_by: [fragment("? desc nulls last", activity.id)],
|
||||||
limit: 1
|
limit: 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,26 @@ def user_delete(conn, %{"nickname" => nickname}) do
|
||||||
|> json(nickname)
|
|> json(nickname)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def user_follow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
|
||||||
|
with %User{} = follower <- User.get_by_nickname(follower_nick),
|
||||||
|
%User{} = followed <- User.get_by_nickname(followed_nick) do
|
||||||
|
User.follow(follower, followed)
|
||||||
|
end
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> json("ok")
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_unfollow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
|
||||||
|
with %User{} = follower <- User.get_by_nickname(follower_nick),
|
||||||
|
%User{} = followed <- User.get_by_nickname(followed_nick) do
|
||||||
|
User.unfollow(follower, followed)
|
||||||
|
end
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> json("ok")
|
||||||
|
end
|
||||||
|
|
||||||
def user_create(
|
def user_create(
|
||||||
conn,
|
conn,
|
||||||
%{"nickname" => nickname, "email" => email, "password" => password}
|
%{"nickname" => nickname, "email" => email, "password" => password}
|
||||||
|
|
|
@ -172,13 +172,16 @@ def post(user, %{"status" => status} = data) do
|
||||||
end)
|
end)
|
||||||
) do
|
) do
|
||||||
res =
|
res =
|
||||||
ActivityPub.create(%{
|
ActivityPub.create(
|
||||||
to: to,
|
%{
|
||||||
actor: user,
|
to: to,
|
||||||
context: context,
|
actor: user,
|
||||||
object: object,
|
context: context,
|
||||||
additional: %{"cc" => cc, "directMessage" => visibility == "direct"}
|
object: object,
|
||||||
})
|
additional: %{"cc" => cc, "directMessage" => visibility == "direct"}
|
||||||
|
},
|
||||||
|
Pleroma.Web.ControllerHelper.truthy_param?(data["preview"]) || false
|
||||||
|
)
|
||||||
|
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,6 +15,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
alias Pleroma.Web.Endpoint
|
alias Pleroma.Web.Endpoint
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
# This is a hack for twidere.
|
# This is a hack for twidere.
|
||||||
def get_by_id_or_ap_id(id) do
|
def get_by_id_or_ap_id(id) do
|
||||||
activity =
|
activity =
|
||||||
|
@ -240,15 +242,21 @@ def format_asctime(date) do
|
||||||
Strftime.strftime!(date, "%a %b %d %H:%M:%S %z %Y")
|
Strftime.strftime!(date, "%a %b %d %H:%M:%S %z %Y")
|
||||||
end
|
end
|
||||||
|
|
||||||
def date_to_asctime(date) do
|
def date_to_asctime(date) when is_binary(date) do
|
||||||
with {:ok, date, _offset} <- date |> DateTime.from_iso8601() do
|
with {:ok, date, _offset} <- DateTime.from_iso8601(date) do
|
||||||
format_asctime(date)
|
format_asctime(date)
|
||||||
else
|
else
|
||||||
_e ->
|
_e ->
|
||||||
|
Logger.warn("Date #{date} in wrong format, must be ISO 8601")
|
||||||
""
|
""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def date_to_asctime(date) do
|
||||||
|
Logger.warn("Date #{date} in wrong format, must be ISO 8601")
|
||||||
|
""
|
||||||
|
end
|
||||||
|
|
||||||
def to_masto_date(%NaiveDateTime{} = date) do
|
def to_masto_date(%NaiveDateTime{} = date) do
|
||||||
date
|
date
|
||||||
|> NaiveDateTime.to_iso8601()
|
|> NaiveDateTime.to_iso8601()
|
||||||
|
|
|
@ -1092,9 +1092,7 @@ def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params)
|
||||||
end
|
end
|
||||||
|
|
||||||
def index(%{assigns: %{user: user}} = conn, _params) do
|
def index(%{assigns: %{user: user}} = conn, _params) do
|
||||||
token =
|
token = get_session(conn, :oauth_token)
|
||||||
conn
|
|
||||||
|> get_session(:oauth_token)
|
|
||||||
|
|
||||||
if user && token do
|
if user && token do
|
||||||
mastodon_emoji = mastodonized_emoji()
|
mastodon_emoji = mastodonized_emoji()
|
||||||
|
@ -1122,7 +1120,8 @@ def index(%{assigns: %{user: user}} = conn, _params) do
|
||||||
auto_play_gif: false,
|
auto_play_gif: false,
|
||||||
display_sensitive_media: false,
|
display_sensitive_media: false,
|
||||||
reduce_motion: false,
|
reduce_motion: false,
|
||||||
max_toot_chars: limit
|
max_toot_chars: limit,
|
||||||
|
mascot: "/images/pleroma-fox-tan-smol.png"
|
||||||
},
|
},
|
||||||
rights: %{
|
rights: %{
|
||||||
delete_others_notice: present?(user.info.is_moderator),
|
delete_others_notice: present?(user.info.is_moderator),
|
||||||
|
@ -1194,6 +1193,7 @@ def index(%{assigns: %{user: user}} = conn, _params) do
|
||||||
|> render("index.html", %{initial_state: initial_state, flavour: flavour})
|
|> render("index.html", %{initial_state: initial_state, flavour: flavour})
|
||||||
else
|
else
|
||||||
conn
|
conn
|
||||||
|
|> put_session(:return_to, conn.request_path)
|
||||||
|> redirect(to: "/web/login")
|
|> redirect(to: "/web/login")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1278,12 +1278,20 @@ def login(conn, _) do
|
||||||
scope: Enum.join(app.scopes, " ")
|
scope: Enum.join(app.scopes, " ")
|
||||||
)
|
)
|
||||||
|
|
||||||
conn
|
redirect(conn, to: path)
|
||||||
|> redirect(to: path)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp local_mastodon_root_path(conn), do: mastodon_api_path(conn, :index, ["getting-started"])
|
defp local_mastodon_root_path(conn) do
|
||||||
|
case get_session(conn, :return_to) do
|
||||||
|
nil ->
|
||||||
|
mastodon_api_path(conn, :index, ["getting-started"])
|
||||||
|
|
||||||
|
return_to ->
|
||||||
|
delete_session(conn, :return_to)
|
||||||
|
return_to
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp get_or_make_app do
|
defp get_or_make_app do
|
||||||
find_attrs = %{client_name: @local_mastodon_name, redirect_uris: "."}
|
find_attrs = %{client_name: @local_mastodon_name, redirect_uris: "."}
|
||||||
|
|
|
@ -147,10 +147,18 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity}
|
||||||
content =
|
content =
|
||||||
object
|
object
|
||||||
|> render_content()
|
|> render_content()
|
||||||
|> HTML.get_cached_scrubbed_html_for_object(
|
|> HTML.get_cached_scrubbed_html_for_activity(
|
||||||
User.html_filter_policy(opts[:for]),
|
User.html_filter_policy(opts[:for]),
|
||||||
activity,
|
activity,
|
||||||
__MODULE__
|
"mastoapi:content"
|
||||||
|
)
|
||||||
|
|
||||||
|
summary =
|
||||||
|
(object["summary"] || "")
|
||||||
|
|> HTML.get_cached_scrubbed_html_for_activity(
|
||||||
|
User.html_filter_policy(opts[:for]),
|
||||||
|
activity,
|
||||||
|
"mastoapi:summary"
|
||||||
)
|
)
|
||||||
|
|
||||||
card = render("card.json", Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity))
|
card = render("card.json", Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity))
|
||||||
|
@ -182,7 +190,7 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity}
|
||||||
muted: CommonAPI.thread_muted?(user, activity) || User.mutes?(opts[:for], user),
|
muted: CommonAPI.thread_muted?(user, activity) || User.mutes?(opts[:for], user),
|
||||||
pinned: pinned?(activity, user),
|
pinned: pinned?(activity, user),
|
||||||
sensitive: sensitive,
|
sensitive: sensitive,
|
||||||
spoiler_text: object["summary"] || "",
|
spoiler_text: summary,
|
||||||
visibility: get_visibility(object),
|
visibility: get_visibility(object),
|
||||||
media_attachments: attachments,
|
media_attachments: attachments,
|
||||||
mentions: mentions,
|
mentions: mentions,
|
||||||
|
|
|
@ -12,7 +12,7 @@ def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do
|
||||||
# html content comes from DB already encoded, decode first and scrub after
|
# html content comes from DB already encoded, decode first and scrub after
|
||||||
|> HtmlEntities.decode()
|
|> HtmlEntities.decode()
|
||||||
|> String.replace(~r/<br\s?\/?>/, " ")
|
|> String.replace(~r/<br\s?\/?>/, " ")
|
||||||
|> HTML.get_cached_stripped_html_for_object(object, __MODULE__)
|
|> HTML.get_cached_stripped_html_for_activity(object, "metadata")
|
||||||
|> Formatter.demojify()
|
|> Formatter.demojify()
|
||||||
|> Formatter.truncate()
|
|> Formatter.truncate()
|
||||||
end
|
end
|
||||||
|
|
|
@ -140,8 +140,12 @@ defmodule Pleroma.Web.Router do
|
||||||
scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
|
scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||||
pipe_through([:admin_api, :oauth_write])
|
pipe_through([:admin_api, :oauth_write])
|
||||||
|
|
||||||
|
post("/user/follow", AdminAPIController, :user_follow)
|
||||||
|
post("/user/unfollow", AdminAPIController, :user_unfollow)
|
||||||
|
|
||||||
get("/users", AdminAPIController, :list_users)
|
get("/users", AdminAPIController, :list_users)
|
||||||
get("/users/:nickname", AdminAPIController, :user_show)
|
get("/users/:nickname", AdminAPIController, :user_show)
|
||||||
|
|
||||||
delete("/user", AdminAPIController, :user_delete)
|
delete("/user", AdminAPIController, :user_delete)
|
||||||
patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
|
patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
|
||||||
post("/user", AdminAPIController, :user_create)
|
post("/user", AdminAPIController, :user_create)
|
||||||
|
|
|
@ -254,10 +254,10 @@ def render(
|
||||||
|
|
||||||
html =
|
html =
|
||||||
content
|
content
|
||||||
|> HTML.get_cached_scrubbed_html_for_object(
|
|> HTML.get_cached_scrubbed_html_for_activity(
|
||||||
User.html_filter_policy(opts[:for]),
|
User.html_filter_policy(opts[:for]),
|
||||||
activity,
|
activity,
|
||||||
__MODULE__
|
"twitterapi:content"
|
||||||
)
|
)
|
||||||
|> Formatter.emojify(object["emoji"])
|
|> Formatter.emojify(object["emoji"])
|
||||||
|
|
||||||
|
@ -265,7 +265,7 @@ def render(
|
||||||
if content do
|
if content do
|
||||||
content
|
content
|
||||||
|> String.replace(~r/<br\s?\/?>/, "\n")
|
|> String.replace(~r/<br\s?\/?>/, "\n")
|
||||||
|> HTML.get_cached_stripped_html_for_object(activity, __MODULE__)
|
|> HTML.get_cached_stripped_html_for_activity(activity, "twitterapi:content")
|
||||||
else
|
else
|
||||||
""
|
""
|
||||||
end
|
end
|
||||||
|
|
7
mix.exs
7
mix.exs
|
@ -41,7 +41,7 @@ def project do
|
||||||
def application do
|
def application do
|
||||||
[
|
[
|
||||||
mod: {Pleroma.Application, []},
|
mod: {Pleroma.Application, []},
|
||||||
extra_applications: [:logger, :runtime_tools, :comeonin],
|
extra_applications: [:logger, :runtime_tools, :comeonin, :quack],
|
||||||
included_applications: [:ex_syslogger]
|
included_applications: [:ex_syslogger]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
@ -93,8 +93,9 @@ defp deps do
|
||||||
{:timex, "~> 3.5"},
|
{:timex, "~> 3.5"},
|
||||||
{:auto_linker,
|
{:auto_linker,
|
||||||
git: "https://git.pleroma.social/pleroma/auto_linker.git",
|
git: "https://git.pleroma.social/pleroma/auto_linker.git",
|
||||||
ref: "94193ca5f97c1f9fdf3d1469653e2d46fac34bcd"},
|
ref: "479dd343f4e563ff91215c8275f3b5c67e032850"},
|
||||||
{:pleroma_job_queue, "~> 0.2.0"}
|
{:pleroma_job_queue, "~> 0.2.0"},
|
||||||
|
{:quack, "~> 0.1.1"}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
3
mix.lock
3
mix.lock
|
@ -1,5 +1,5 @@
|
||||||
%{
|
%{
|
||||||
"auto_linker": {:git, "https://git.pleroma.social/pleroma/auto_linker.git", "94193ca5f97c1f9fdf3d1469653e2d46fac34bcd", [ref: "94193ca5f97c1f9fdf3d1469653e2d46fac34bcd"]},
|
"auto_linker": {:git, "https://git.pleroma.social/pleroma/auto_linker.git", "479dd343f4e563ff91215c8275f3b5c67e032850", [ref: "479dd343f4e563ff91215c8275f3b5c67e032850"]},
|
||||||
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
|
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
|
||||||
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
|
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
|
||||||
"cachex": {:hex, :cachex, "3.0.2", "1351caa4e26e29f7d7ec1d29b53d6013f0447630bbf382b4fb5d5bad0209f203", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
|
"cachex": {:hex, :cachex, "3.0.2", "1351caa4e26e29f7d7ec1d29b53d6013f0447630bbf382b4fb5d5bad0209f203", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
@ -57,6 +57,7 @@
|
||||||
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
|
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
|
||||||
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm"},
|
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm"},
|
||||||
"postgrex": {:hex, :postgrex, "0.14.1", "63247d4a5ad6b9de57a0bac5d807e1c32d41e39c04b8a4156a26c63bcd8a2e49", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
"postgrex": {:hex, :postgrex, "0.14.1", "63247d4a5ad6b9de57a0bac5d807e1c32d41e39c04b8a4156a26c63bcd8a2e49", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
|
"quack": {:hex, :quack, "0.1.1", "cca7b4da1a233757fdb44b3334fce80c94785b3ad5a602053b7a002b5a8967bf", [:mix], [{:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "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"},
|
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
|
||||||
"swoosh": {:hex, :swoosh, "0.20.0", "9a6c13822c9815993c03b6f8fccc370fcffb3c158d9754f67b1fdee6b3a5d928", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.12", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"},
|
"swoosh": {:hex, :swoosh, "0.20.0", "9a6c13822c9815993c03b6f8fccc370fcffb3c158d9754f67b1fdee6b3a5d928", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.12", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddOauthTokenIndexes do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create(unique_index(:oauth_tokens, [:token]))
|
||||||
|
create(index(:oauth_tokens, [:app_id]))
|
||||||
|
create(index(:oauth_tokens, [:user_id]))
|
||||||
|
end
|
||||||
|
end
|
|
@ -240,6 +240,16 @@ def oauth_token_factory do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def oauth_authorization_factory do
|
||||||
|
%Pleroma.Web.OAuth.Authorization{
|
||||||
|
token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false),
|
||||||
|
scopes: ["read", "write", "follow", "push"],
|
||||||
|
valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10),
|
||||||
|
user: build(:user),
|
||||||
|
app: build(:oauth_app)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def push_subscription_factory do
|
def push_subscription_factory do
|
||||||
%Pleroma.Web.Push.Subscription{
|
%Pleroma.Web.Push.Subscription{
|
||||||
user: build(:user),
|
user: build(:user),
|
||||||
|
|
|
@ -635,16 +635,6 @@ test "works with base64 encoded images" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "fetch the latest Follow" do
|
|
||||||
test "fetches the latest Follow activity" do
|
|
||||||
%Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
|
|
||||||
follower = User.get_by_ap_id(activity.data["actor"])
|
|
||||||
followed = User.get_by_ap_id(activity.data["object"])
|
|
||||||
|
|
||||||
assert activity == Utils.fetch_latest_follow(follower, followed)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "fetching an object" do
|
describe "fetching an object" do
|
||||||
test "it fetches an object" do
|
test "it fetches an object" do
|
||||||
{:ok, object} =
|
{:ok, object} =
|
||||||
|
|
|
@ -1,10 +1,34 @@
|
||||||
defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
describe "fetch the latest Follow" do
|
||||||
|
test "fetches the latest Follow activity" do
|
||||||
|
%Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
|
||||||
|
follower = Repo.get_by(User, ap_id: activity.data["actor"])
|
||||||
|
followed = Repo.get_by(User, ap_id: activity.data["object"])
|
||||||
|
|
||||||
|
assert activity == Utils.fetch_latest_follow(follower, followed)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "fetch the latest Block" do
|
||||||
|
test "fetches the latest Block activity" do
|
||||||
|
blocker = insert(:user)
|
||||||
|
blocked = insert(:user)
|
||||||
|
{:ok, activity} = ActivityPub.block(blocker, blocked)
|
||||||
|
|
||||||
|
assert activity == Utils.fetch_latest_block(blocker, blocked)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "determine_explicit_mentions()" do
|
describe "determine_explicit_mentions()" do
|
||||||
test "works with an object that has mentions" do
|
test "works with an object that has mentions" do
|
||||||
object = %{
|
object = %{
|
||||||
|
|
|
@ -74,6 +74,52 @@ test "when the user doesn't exist", %{conn: conn} do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "/api/pleroma/admin/user/follow" do
|
||||||
|
test "allows to force-follow another user" do
|
||||||
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
user = insert(:user)
|
||||||
|
follower = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/user/follow", %{
|
||||||
|
"follower" => follower.nickname,
|
||||||
|
"followed" => user.nickname
|
||||||
|
})
|
||||||
|
|
||||||
|
user = User.get_by_id(user.id)
|
||||||
|
follower = User.get_by_id(follower.id)
|
||||||
|
|
||||||
|
assert User.following?(follower, user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "/api/pleroma/admin/user/unfollow" do
|
||||||
|
test "allows to force-unfollow another user" do
|
||||||
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
user = insert(:user)
|
||||||
|
follower = insert(:user)
|
||||||
|
|
||||||
|
User.follow(follower, user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/user/unfollow", %{
|
||||||
|
"follower" => follower.nickname,
|
||||||
|
"followed" => user.nickname
|
||||||
|
})
|
||||||
|
|
||||||
|
user = User.get_by_id(user.id)
|
||||||
|
follower = User.get_by_id(follower.id)
|
||||||
|
|
||||||
|
refute User.following?(follower, user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "PUT /api/pleroma/admin/users/tag" do
|
describe "PUT /api/pleroma/admin/users/tag" do
|
||||||
setup do
|
setup do
|
||||||
admin = insert(:user, info: %{is_admin: true})
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
|
|
@ -153,4 +153,40 @@ test "returns an existing mapping for an existing object" do
|
||||||
assert conversation_id == object.id
|
assert conversation_id == object.id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "formats date to asctime" do
|
||||||
|
test "when date is in ISO 8601 format" do
|
||||||
|
date = DateTime.utc_now() |> DateTime.to_iso8601()
|
||||||
|
|
||||||
|
expected =
|
||||||
|
date
|
||||||
|
|> DateTime.from_iso8601()
|
||||||
|
|> elem(1)
|
||||||
|
|> Calendar.Strftime.strftime!("%a %b %d %H:%M:%S %z %Y")
|
||||||
|
|
||||||
|
assert Utils.date_to_asctime(date) == expected
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when date is a binary in wrong format" do
|
||||||
|
date = DateTime.utc_now()
|
||||||
|
|
||||||
|
expected = ""
|
||||||
|
|
||||||
|
assert Utils.date_to_asctime(date) == expected
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when date is a Unix timestamp" do
|
||||||
|
date = DateTime.utc_now() |> DateTime.to_unix()
|
||||||
|
|
||||||
|
expected = ""
|
||||||
|
|
||||||
|
assert Utils.date_to_asctime(date) == expected
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when date is nil" do
|
||||||
|
expected = ""
|
||||||
|
|
||||||
|
assert Utils.date_to_asctime(nil) == expected
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -143,6 +143,55 @@ test "posting a sensitive status", %{conn: conn} do
|
||||||
assert Activity.get_by_id(id)
|
assert Activity.get_by_id(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "posting a fake status", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
real_conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/statuses", %{
|
||||||
|
"status" =>
|
||||||
|
"\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it"
|
||||||
|
})
|
||||||
|
|
||||||
|
real_status = json_response(real_conn, 200)
|
||||||
|
|
||||||
|
assert real_status
|
||||||
|
assert Object.get_by_ap_id(real_status["uri"])
|
||||||
|
|
||||||
|
real_status =
|
||||||
|
real_status
|
||||||
|
|> Map.put("id", nil)
|
||||||
|
|> Map.put("url", nil)
|
||||||
|
|> Map.put("uri", nil)
|
||||||
|
|> Map.put("created_at", nil)
|
||||||
|
|> Kernel.put_in(["pleroma", "conversation_id"], nil)
|
||||||
|
|
||||||
|
fake_conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/statuses", %{
|
||||||
|
"status" =>
|
||||||
|
"\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it",
|
||||||
|
"preview" => true
|
||||||
|
})
|
||||||
|
|
||||||
|
fake_status = json_response(fake_conn, 200)
|
||||||
|
|
||||||
|
assert fake_status
|
||||||
|
refute Object.get_by_ap_id(fake_status["uri"])
|
||||||
|
|
||||||
|
fake_status =
|
||||||
|
fake_status
|
||||||
|
|> Map.put("id", nil)
|
||||||
|
|> Map.put("url", nil)
|
||||||
|
|> Map.put("uri", nil)
|
||||||
|
|> Map.put("created_at", nil)
|
||||||
|
|> Kernel.put_in(["pleroma", "conversation_id"], nil)
|
||||||
|
|
||||||
|
assert real_status == fake_status
|
||||||
|
end
|
||||||
|
|
||||||
test "posting a status with OGP link preview", %{conn: conn} do
|
test "posting a status with OGP link preview", %{conn: conn} do
|
||||||
Pleroma.Config.put([:rich_media, :enabled], true)
|
Pleroma.Config.put([:rich_media, :enabled], true)
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
@ -2307,4 +2356,71 @@ test "with tags", %{conn: conn} do
|
||||||
assert Map.has_key?(emoji, "visible_in_picker")
|
assert Map.has_key?(emoji, "visible_in_picker")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "index/2 redirections" do
|
||||||
|
setup %{conn: conn} do
|
||||||
|
session_opts = [
|
||||||
|
store: :cookie,
|
||||||
|
key: "_test",
|
||||||
|
signing_salt: "cooldude"
|
||||||
|
]
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> Plug.Session.call(Plug.Session.init(session_opts))
|
||||||
|
|> fetch_session()
|
||||||
|
|
||||||
|
test_path = "/web/statuses/test"
|
||||||
|
%{conn: conn, path: test_path}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
|
||||||
|
conn = get(conn, path)
|
||||||
|
|
||||||
|
assert conn.status == 302
|
||||||
|
assert redirected_to(conn) == "/web/login"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
|
||||||
|
token = insert(:oauth_token)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, token.user)
|
||||||
|
|> put_session(:oauth_token, token.token)
|
||||||
|
|> get(path)
|
||||||
|
|
||||||
|
assert conn.status == 200
|
||||||
|
end
|
||||||
|
|
||||||
|
test "saves referer path to session", %{conn: conn, path: path} do
|
||||||
|
conn = get(conn, path)
|
||||||
|
return_to = Plug.Conn.get_session(conn, :return_to)
|
||||||
|
|
||||||
|
assert return_to == path
|
||||||
|
end
|
||||||
|
|
||||||
|
test "redirects to the saved path after log in", %{conn: conn, path: path} do
|
||||||
|
app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
|
||||||
|
auth = insert(:oauth_authorization, app: app)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_session(:return_to, path)
|
||||||
|
|> get("/web/login", %{code: auth.token})
|
||||||
|
|
||||||
|
assert conn.status == 302
|
||||||
|
assert redirected_to(conn) == path
|
||||||
|
end
|
||||||
|
|
||||||
|
test "redirects to the getting-started page when referer is not present", %{conn: conn} do
|
||||||
|
app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
|
||||||
|
auth = insert(:oauth_authorization, app: app)
|
||||||
|
|
||||||
|
conn = get(conn, "/web/login", %{code: auth.token})
|
||||||
|
|
||||||
|
assert conn.status == 302
|
||||||
|
assert redirected_to(conn) == "/web/getting-started"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue