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",
|
||||
metadata: [:request_id]
|
||||
|
||||
config :quack,
|
||||
level: :warn,
|
||||
meta: [:all],
|
||||
webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
|
||||
|
||||
config :mime, :types, %{
|
||||
"application/xml" => ["xml"],
|
||||
"application/xrd+xml" => ["xrd+xml"],
|
||||
|
|
|
@ -58,6 +58,26 @@ Authentication is required and the user must be an admin.
|
|||
- `password`
|
||||
- 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`
|
||||
|
||||
### Toggle user activation
|
||||
|
|
|
@ -44,3 +44,9 @@ 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
|
||||
|
||||
## 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`)
|
||||
|
||||
## :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:
|
||||
```
|
||||
|
@ -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/)
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -81,6 +81,14 @@ def run(["gen" | rest]) do
|
|||
|
||||
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 =
|
||||
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}.")
|
||||
File.write(psql_path, result_psql)
|
||||
|
||||
write_robots_txt(indexable)
|
||||
|
||||
Mix.shell().info(
|
||||
"\n" <>
|
||||
"""
|
||||
|
@ -163,4 +173,28 @@ def run(["gen" | rest]) do
|
|||
)
|
||||
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
|
||||
|
|
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 strip_tags(html), do: Scrubber.scrub(html, Scrubber.StripTags)
|
||||
|
||||
def get_cached_scrubbed_html_for_object(content, scrubbers, object, module) do
|
||||
key = "#{module}#{generate_scrubber_signature(scrubbers)}|#{object.id}"
|
||||
Cachex.fetch!(:scrubber_cache, key, fn _key -> ensure_scrubbed_html(content, scrubbers) end)
|
||||
def get_cached_scrubbed_html_for_activity(content, scrubbers, activity, key \\ "") do
|
||||
key = "#{key}#{generate_scrubber_signature(scrubbers)}|#{activity.id}"
|
||||
|
||||
Cachex.fetch!(:scrubber_cache, key, fn _key ->
|
||||
ensure_scrubbed_html(content, scrubbers, activity.data["object"]["fake"] || false)
|
||||
end)
|
||||
end
|
||||
|
||||
def get_cached_stripped_html_for_object(content, object, module) do
|
||||
get_cached_scrubbed_html_for_object(
|
||||
def get_cached_stripped_html_for_activity(content, activity, key) do
|
||||
get_cached_scrubbed_html_for_activity(
|
||||
content,
|
||||
HtmlSanitizeEx.Scrubber.StripTags,
|
||||
object,
|
||||
module
|
||||
activity,
|
||||
key
|
||||
)
|
||||
end
|
||||
|
||||
def ensure_scrubbed_html(
|
||||
content,
|
||||
scrubbers
|
||||
scrubbers,
|
||||
false = _fake
|
||||
) do
|
||||
{:commit, filter_tags(content, scrubbers)}
|
||||
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
|
||||
generate_scrubber_signature([scrubber])
|
||||
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!
|
||||
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
|
||||
# preloaded.
|
||||
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 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),
|
||||
map <- lazy_put_activity_defaults(map),
|
||||
map <- lazy_put_activity_defaults(map, fake),
|
||||
:ok <- check_actor_is_active(map["actor"]),
|
||||
{_, true} <- {:remote_limit_error, check_remote_limit(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
|
||||
{recipients, _, _} = get_recipients(map)
|
||||
|
||||
{:ok, activity} =
|
||||
Repo.insert(%Activity{
|
||||
data: map,
|
||||
|
@ -146,8 +146,23 @@ def insert(map, local \\ true) when is_map(map) do
|
|||
stream_out(activity)
|
||||
{:ok, activity}
|
||||
else
|
||||
%Activity{} = activity -> {:ok, activity}
|
||||
error -> {:error, error}
|
||||
%Activity{} = activity ->
|
||||
{: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
|
||||
|
||||
|
@ -190,7 +205,7 @@ def stream_out(activity) do
|
|||
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] || %{}
|
||||
# only accept false as false value
|
||||
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},
|
||||
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),
|
||||
# Changing note count prior to enqueuing federation task in order to avoid
|
||||
# race conditions on updating user.info
|
||||
{:ok, _actor} <- increase_note_count_if_public(actor, activity),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
else
|
||||
{:fake, true, activity} ->
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -175,18 +175,26 @@ def maybe_federate(_), do: :ok
|
|||
Adds an id and a published data if they aren't there,
|
||||
also adds it to an included object
|
||||
"""
|
||||
def lazy_put_activity_defaults(map) do
|
||||
%{data: %{"id" => context}, id: context_id} = create_context(map["context"])
|
||||
|
||||
def lazy_put_activity_defaults(map, fake \\ false) do
|
||||
map =
|
||||
map
|
||||
|> 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)
|
||||
unless fake do
|
||||
%{data: %{"id" => context}, id: context_id} = create_context(map["context"])
|
||||
|
||||
map
|
||||
|> 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
|
||||
object = lazy_put_object_defaults(map["object"], map)
|
||||
object = lazy_put_object_defaults(map["object"], map, fake)
|
||||
%{map | "object" => object}
|
||||
else
|
||||
map
|
||||
|
@ -196,7 +204,18 @@ def lazy_put_activity_defaults(map) do
|
|||
@doc """
|
||||
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.put_new_lazy("id", &generate_object_id/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
|
||||
),
|
||||
where: activity.actor == ^follower_id,
|
||||
# this is to use the index
|
||||
where:
|
||||
fragment(
|
||||
"? @> ?",
|
||||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||
activity.data,
|
||||
^%{object: followed_id}
|
||||
activity.data,
|
||||
^followed_id
|
||||
),
|
||||
order_by: [desc: :id],
|
||||
order_by: [fragment("? desc nulls last", activity.id)],
|
||||
limit: 1
|
||||
)
|
||||
|
||||
|
@ -567,13 +588,15 @@ def fetch_latest_block(%User{ap_id: blocker_id}, %User{ap_id: blocked_id}) do
|
|||
activity.data
|
||||
),
|
||||
where: activity.actor == ^blocker_id,
|
||||
# this is to use the index
|
||||
where:
|
||||
fragment(
|
||||
"? @> ?",
|
||||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||
activity.data,
|
||||
^%{object: blocked_id}
|
||||
activity.data,
|
||||
^blocked_id
|
||||
),
|
||||
order_by: [desc: :id],
|
||||
order_by: [fragment("? desc nulls last", activity.id)],
|
||||
limit: 1
|
||||
)
|
||||
|
||||
|
|
|
@ -25,6 +25,26 @@ def user_delete(conn, %{"nickname" => nickname}) do
|
|||
|> json(nickname)
|
||||
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(
|
||||
conn,
|
||||
%{"nickname" => nickname, "email" => email, "password" => password}
|
||||
|
|
|
@ -172,13 +172,16 @@ def post(user, %{"status" => status} = data) do
|
|||
end)
|
||||
) do
|
||||
res =
|
||||
ActivityPub.create(%{
|
||||
to: to,
|
||||
actor: user,
|
||||
context: context,
|
||||
object: object,
|
||||
additional: %{"cc" => cc, "directMessage" => visibility == "direct"}
|
||||
})
|
||||
ActivityPub.create(
|
||||
%{
|
||||
to: to,
|
||||
actor: user,
|
||||
context: context,
|
||||
object: object,
|
||||
additional: %{"cc" => cc, "directMessage" => visibility == "direct"}
|
||||
},
|
||||
Pleroma.Web.ControllerHelper.truthy_param?(data["preview"]) || false
|
||||
)
|
||||
|
||||
res
|
||||
end
|
||||
|
|
|
@ -15,6 +15,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
require Logger
|
||||
|
||||
# This is a hack for twidere.
|
||||
def get_by_id_or_ap_id(id) do
|
||||
activity =
|
||||
|
@ -240,15 +242,21 @@ def format_asctime(date) do
|
|||
Strftime.strftime!(date, "%a %b %d %H:%M:%S %z %Y")
|
||||
end
|
||||
|
||||
def date_to_asctime(date) do
|
||||
with {:ok, date, _offset} <- date |> DateTime.from_iso8601() do
|
||||
def date_to_asctime(date) when is_binary(date) do
|
||||
with {:ok, date, _offset} <- DateTime.from_iso8601(date) do
|
||||
format_asctime(date)
|
||||
else
|
||||
_e ->
|
||||
Logger.warn("Date #{date} in wrong format, must be ISO 8601")
|
||||
""
|
||||
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
|
||||
date
|
||||
|> NaiveDateTime.to_iso8601()
|
||||
|
|
|
@ -1092,9 +1092,7 @@ def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params)
|
|||
end
|
||||
|
||||
def index(%{assigns: %{user: user}} = conn, _params) do
|
||||
token =
|
||||
conn
|
||||
|> get_session(:oauth_token)
|
||||
token = get_session(conn, :oauth_token)
|
||||
|
||||
if user && token do
|
||||
mastodon_emoji = mastodonized_emoji()
|
||||
|
@ -1122,7 +1120,8 @@ def index(%{assigns: %{user: user}} = conn, _params) do
|
|||
auto_play_gif: false,
|
||||
display_sensitive_media: false,
|
||||
reduce_motion: false,
|
||||
max_toot_chars: limit
|
||||
max_toot_chars: limit,
|
||||
mascot: "/images/pleroma-fox-tan-smol.png"
|
||||
},
|
||||
rights: %{
|
||||
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})
|
||||
else
|
||||
conn
|
||||
|> put_session(:return_to, conn.request_path)
|
||||
|> redirect(to: "/web/login")
|
||||
end
|
||||
end
|
||||
|
@ -1278,12 +1278,20 @@ def login(conn, _) do
|
|||
scope: Enum.join(app.scopes, " ")
|
||||
)
|
||||
|
||||
conn
|
||||
|> redirect(to: path)
|
||||
redirect(conn, to: path)
|
||||
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
|
||||
find_attrs = %{client_name: @local_mastodon_name, redirect_uris: "."}
|
||||
|
|
|
@ -147,10 +147,18 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity}
|
|||
content =
|
||||
object
|
||||
|> render_content()
|
||||
|> HTML.get_cached_scrubbed_html_for_object(
|
||||
|> HTML.get_cached_scrubbed_html_for_activity(
|
||||
User.html_filter_policy(opts[:for]),
|
||||
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))
|
||||
|
@ -182,7 +190,7 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity}
|
|||
muted: CommonAPI.thread_muted?(user, activity) || User.mutes?(opts[:for], user),
|
||||
pinned: pinned?(activity, user),
|
||||
sensitive: sensitive,
|
||||
spoiler_text: object["summary"] || "",
|
||||
spoiler_text: summary,
|
||||
visibility: get_visibility(object),
|
||||
media_attachments: attachments,
|
||||
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
|
||||
|> HtmlEntities.decode()
|
||||
|> 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.truncate()
|
||||
end
|
||||
|
|
|
@ -140,8 +140,12 @@ defmodule Pleroma.Web.Router do
|
|||
scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||
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/:nickname", AdminAPIController, :user_show)
|
||||
|
||||
delete("/user", AdminAPIController, :user_delete)
|
||||
patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
|
||||
post("/user", AdminAPIController, :user_create)
|
||||
|
|
|
@ -254,10 +254,10 @@ def render(
|
|||
|
||||
html =
|
||||
content
|
||||
|> HTML.get_cached_scrubbed_html_for_object(
|
||||
|> HTML.get_cached_scrubbed_html_for_activity(
|
||||
User.html_filter_policy(opts[:for]),
|
||||
activity,
|
||||
__MODULE__
|
||||
"twitterapi:content"
|
||||
)
|
||||
|> Formatter.emojify(object["emoji"])
|
||||
|
||||
|
@ -265,7 +265,7 @@ def render(
|
|||
if content do
|
||||
content
|
||||
|> 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
|
||||
""
|
||||
end
|
||||
|
|
7
mix.exs
7
mix.exs
|
@ -41,7 +41,7 @@ def project do
|
|||
def application do
|
||||
[
|
||||
mod: {Pleroma.Application, []},
|
||||
extra_applications: [:logger, :runtime_tools, :comeonin],
|
||||
extra_applications: [:logger, :runtime_tools, :comeonin, :quack],
|
||||
included_applications: [:ex_syslogger]
|
||||
]
|
||||
end
|
||||
|
@ -93,8 +93,9 @@ defp deps do
|
|||
{:timex, "~> 3.5"},
|
||||
{:auto_linker,
|
||||
git: "https://git.pleroma.social/pleroma/auto_linker.git",
|
||||
ref: "94193ca5f97c1f9fdf3d1469653e2d46fac34bcd"},
|
||||
{:pleroma_job_queue, "~> 0.2.0"}
|
||||
ref: "479dd343f4e563ff91215c8275f3b5c67e032850"},
|
||||
{:pleroma_job_queue, "~> 0.2.0"},
|
||||
{:quack, "~> 0.1.1"}
|
||||
]
|
||||
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"},
|
||||
"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"},
|
||||
|
@ -57,6 +57,7 @@
|
|||
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "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"},
|
||||
"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"},
|
||||
"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"},
|
||||
|
|
|
@ -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
|
||||
|
||||
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
|
||||
%Pleroma.Web.Push.Subscription{
|
||||
user: build(:user),
|
||||
|
|
|
@ -635,16 +635,6 @@ test "works with base64 encoded images" do
|
|||
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
|
||||
test "it fetches an object" do
|
||||
{:ok, object} =
|
||||
|
|
|
@ -1,10 +1,34 @@
|
|||
defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
||||
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.CommonAPI
|
||||
|
||||
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
|
||||
test "works with an object that has mentions" do
|
||||
object = %{
|
||||
|
|
|
@ -74,6 +74,52 @@ test "when the user doesn't exist", %{conn: conn} do
|
|||
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
|
||||
setup do
|
||||
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
|
||||
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
|
||||
|
|
|
@ -143,6 +143,55 @@ test "posting a sensitive status", %{conn: conn} do
|
|||
assert Activity.get_by_id(id)
|
||||
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
|
||||
Pleroma.Config.put([:rich_media, :enabled], true)
|
||||
user = insert(:user)
|
||||
|
@ -2307,4 +2356,71 @@ test "with tags", %{conn: conn} do
|
|||
assert Map.has_key?(emoji, "visible_in_picker")
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue