Browse Source

Merge develop to 770-add-emoji-tags

Merge conflict in test/web/mastodon_api/mastodon_api_controller_test.exs
tags/v1.1.4
Alex S 5 years ago
parent
commit
7410aee886
27 changed files with 498 additions and 70 deletions
  1. +5
    -0
      config/config.exs
  2. +20
    -0
      docs/api/admin_api.md
  3. +6
    -0
      docs/api/differences_in_mastoapi_responses.md
  4. +19
    -1
      docs/config.md
  5. +34
    -0
      lib/mix/tasks/pleroma/instance.ex
  6. +2
    -0
      lib/mix/tasks/pleroma/robots_txt.eex
  7. +20
    -8
      lib/pleroma/html.ex
  8. +5
    -0
      lib/pleroma/object.ex
  9. +27
    -8
      lib/pleroma/web/activity_pub/activity_pub.ex
  10. +39
    -16
      lib/pleroma/web/activity_pub/utils.ex
  11. +20
    -0
      lib/pleroma/web/admin_api/admin_api_controller.ex
  12. +10
    -7
      lib/pleroma/web/common_api/common_api.ex
  13. +10
    -2
      lib/pleroma/web/common_api/utils.ex
  14. +15
    -7
      lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
  15. +11
    -3
      lib/pleroma/web/mastodon_api/views/status_view.ex
  16. +1
    -1
      lib/pleroma/web/metadata/utils.ex
  17. +4
    -0
      lib/pleroma/web/router.ex
  18. +3
    -3
      lib/pleroma/web/twitter_api/views/activity_view.ex
  19. +4
    -3
      mix.exs
  20. +2
    -1
      mix.lock
  21. +9
    -0
      priv/repo/migrations/20190403131720_add_oauth_token_indexes.exs
  22. +10
    -0
      test/support/factory.ex
  23. +0
    -10
      test/web/activity_pub/activity_pub_test.exs
  24. +24
    -0
      test/web/activity_pub/utils_test.exs
  25. +46
    -0
      test/web/admin_api/admin_api_controller_test.exs
  26. +36
    -0
      test/web/common_api/common_api_utils_test.exs
  27. +116
    -0
      test/web/mastodon_api/mastodon_api_controller_test.exs

+ 5
- 0
config/config.exs View File

@@ -124,6 +124,11 @@ config :logger, :ex_syslogger,
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"],


+ 20
- 0
docs/api/admin_api.md View File

@@ -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


+ 6
- 0
docs/api/differences_in_mastoapi_responses.md View File

@@ -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.

+ 19
- 1
docs/config.md View File

@@ -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




+ 34
- 0
lib/mix/tasks/pleroma/instance.ex View File

@@ -81,6 +81,14 @@ defmodule Mix.Tasks.Pleroma.Instance 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 @@ defmodule Mix.Tasks.Pleroma.Instance 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 @@ defmodule Mix.Tasks.Pleroma.Instance 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
- 0
lib/mix/tasks/pleroma/robots_txt.eex View File

@@ -0,0 +1,2 @@
User-Agent: *
Disallow: <%= if indexable, do: "", else: "/" %>

+ 20
- 8
lib/pleroma/html.ex View File

@@ -28,27 +28,39 @@ defmodule Pleroma.HTML do
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
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 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, content,
HtmlSanitizeEx.Scrubber.StripTags, HtmlSanitizeEx.Scrubber.StripTags,
object,
module
activity,
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


+ 5
- 0
lib/pleroma/object.ex View File

@@ -44,6 +44,11 @@ defmodule Pleroma.Object 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


+ 27
- 8
lib/pleroma/web/activity_pub/activity_pub.ex View File

@@ -113,15 +113,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do


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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
stream_out(activity) stream_out(activity)
{:ok, activity} {:ok, activity}
else 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
end end


@@ -190,7 +205,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
%{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




+ 39
- 16
lib/pleroma/web/activity_pub/utils.ex View File

@@ -175,18 +175,26 @@ defmodule Pleroma.Web.ActivityPub.Utils do
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
%{data: %{"id" => context}, id: context_id} = create_context(map["context"])

def lazy_put_activity_defaults(map, fake \\ false) do
map = 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 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 @@ defmodule Pleroma.Web.ActivityPub.Utils 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 @@ defmodule Pleroma.Web.ActivityPub.Utils 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 @@ defmodule Pleroma.Web.ActivityPub.Utils 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, activity.data,
^%{object: blocked_id}
^blocked_id
), ),
order_by: [desc: :id],
order_by: [fragment("? desc nulls last", activity.id)],
limit: 1 limit: 1
) )




+ 20
- 0
lib/pleroma/web/admin_api/admin_api_controller.ex View File

@@ -25,6 +25,26 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController 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}


+ 10
- 7
lib/pleroma/web/common_api/common_api.ex View File

@@ -172,13 +172,16 @@ defmodule Pleroma.Web.CommonAPI do
end) end)
) do ) do
res = 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 res
end end


+ 10
- 2
lib/pleroma/web/common_api/utils.ex View File

@@ -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 @@ defmodule Pleroma.Web.CommonAPI.Utils 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
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) 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()


+ 15
- 7
lib/pleroma/web/mastodon_api/mastodon_api_controller.ex View File

@@ -1092,9 +1092,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end end


def index(%{assigns: %{user: user}} = conn, _params) do def index(%{assigns: %{user: user}} = conn, _params) do
token =
conn
|> get_session(:oauth_token)
token = get_session(conn, :oauth_token)


if user && token do if user && token do
mastodon_emoji = mastodonized_emoji() mastodon_emoji = mastodonized_emoji()
@@ -1122,7 +1120,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
scope: Enum.join(app.scopes, " ") scope: Enum.join(app.scopes, " ")
) )


conn
|> redirect(to: path)
redirect(conn, 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: "."}


+ 11
- 3
lib/pleroma/web/mastodon_api/views/status_view.ex View File

@@ -147,10 +147,18 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
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 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
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,


+ 1
- 1
lib/pleroma/web/metadata/utils.ex View File

@@ -12,7 +12,7 @@ defmodule Pleroma.Web.Metadata.Utils 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


+ 4
- 0
lib/pleroma/web/router.ex View File

@@ -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)


+ 3
- 3
lib/pleroma/web/twitter_api/views/activity_view.ex View File

@@ -254,10 +254,10 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do


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 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
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


+ 4
- 3
mix.exs View File

@@ -41,7 +41,7 @@ defmodule Pleroma.Mixfile 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 @@ defmodule Pleroma.Mixfile 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"},
{:pleroma_job_queue, "~> 0.2.0"}
ref: "479dd343f4e563ff91215c8275f3b5c67e032850"},
{:pleroma_job_queue, "~> 0.2.0"},
{:quack, "~> 0.1.1"}
] ]
end end




+ 2
- 1
mix.lock View File

@@ -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"},


+ 9
- 0
priv/repo/migrations/20190403131720_add_oauth_token_indexes.exs View File

@@ -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

+ 10
- 0
test/support/factory.ex View File

@@ -240,6 +240,16 @@ defmodule Pleroma.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),


+ 0
- 10
test/web/activity_pub/activity_pub_test.exs View File

@@ -635,16 +635,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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} =


+ 24
- 0
test/web/activity_pub/utils_test.exs View File

@@ -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 = %{


+ 46
- 0
test/web/admin_api/admin_api_controller_test.exs View File

@@ -74,6 +74,52 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest 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})


+ 36
- 0
test/web/common_api/common_api_utils_test.exs View File

@@ -153,4 +153,40 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest 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

+ 116
- 0
test/web/mastodon_api/mastodon_api_controller_test.exs View File

@@ -143,6 +143,55 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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…
Cancel
Save