Browse Source

Merge branch 'develop' into 'fix/admin-api-user-deletion'

# Conflicts:
#   CHANGELOG.md
tags/v1.1.4
Maxim Filippov 4 years ago
parent
commit
ec969eec51
93 changed files with 1281 additions and 430 deletions
  1. +13
    -0
      CHANGELOG.md
  2. +6
    -1
      config/config.exs
  3. +8
    -1
      docs/config.md
  4. +36
    -0
      lib/mix/tasks/pleroma/database.ex
  5. +25
    -2
      lib/pleroma/activity.ex
  6. +63
    -129
      lib/pleroma/application.ex
  7. +1
    -1
      lib/pleroma/captcha/captcha.ex
  8. +1
    -1
      lib/pleroma/config/transfer_task.ex
  9. +1
    -1
      lib/pleroma/emoji.ex
  10. +1
    -1
      lib/pleroma/flake_id.ex
  11. +1
    -1
      lib/pleroma/gopher/server.ex
  12. +1
    -1
      lib/pleroma/scheduled_activity_worker.ex
  13. +43
    -17
      lib/pleroma/stats.ex
  14. +2
    -2
      lib/pleroma/uploaders/local.ex
  15. +2
    -0
      lib/pleroma/uploaders/mdii.ex
  16. +7
    -5
      lib/pleroma/uploaders/s3.ex
  17. +21
    -17
      lib/pleroma/user.ex
  18. +10
    -0
      lib/pleroma/web/activity_pub/activity_pub.ex
  19. +33
    -1
      lib/pleroma/web/activity_pub/mrf.ex
  20. +3
    -0
      lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex
  21. +6
    -0
      lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex
  22. +3
    -0
      lib/pleroma/web/activity_pub/mrf/drop_policy.ex
  23. +2
    -0
      lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex
  24. +4
    -0
      lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
  25. +32
    -0
      lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
  26. +3
    -0
      lib/pleroma/web/activity_pub/mrf/mediaproxy_warming_policy.ex
  27. +3
    -0
      lib/pleroma/web/activity_pub/mrf/mention_policy.ex
  28. +3
    -0
      lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex
  29. +3
    -0
      lib/pleroma/web/activity_pub/mrf/noop_policy.ex
  30. +2
    -0
      lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
  31. +4
    -0
      lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
  32. +12
    -0
      lib/pleroma/web/activity_pub/mrf/simple_policy.ex
  33. +3
    -0
      lib/pleroma/web/activity_pub/mrf/subchain_policy.ex
  34. +3
    -0
      lib/pleroma/web/activity_pub/mrf/tag_policy.ex
  35. +9
    -0
      lib/pleroma/web/activity_pub/mrf/user_allowlist_policy.ex
  36. +37
    -0
      lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex
  37. +2
    -1
      lib/pleroma/web/activity_pub/publisher.ex
  38. +15
    -2
      lib/pleroma/web/activity_pub/relay.ex
  39. +10
    -40
      lib/pleroma/web/activity_pub/transmogrifier.ex
  40. +3
    -1
      lib/pleroma/web/chat_channel.ex
  41. +1
    -1
      lib/pleroma/web/federator/retry_queue.ex
  42. +14
    -10
      lib/pleroma/web/mastodon_api/mastodon_api.ex
  43. +26
    -36
      lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
  44. +9
    -2
      lib/pleroma/web/mastodon_api/views/account_view.ex
  45. +15
    -7
      lib/pleroma/web/mastodon_api/views/status_view.ex
  46. +4
    -50
      lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
  47. +10
    -16
      lib/pleroma/web/oauth/token/clean_worker.ex
  48. +6
    -13
      lib/pleroma/web/streamer.ex
  49. +2
    -2
      lib/pleroma/web/web.ex
  50. +3
    -2
      mix.exs
  51. +2
    -1
      mix.lock
  52. +1
    -0
      priv/static/adminfe/chunk-0e18.e12401fb.css
  53. +1
    -1
      priv/static/adminfe/chunk-1fbf.d7a1893c.css
  54. +0
    -0
      priv/static/adminfe/chunk-2325.0d22684d.css
  55. +1
    -0
      priv/static/adminfe/chunk-5e57.ac97b15a.css
  56. +1
    -1
      priv/static/adminfe/chunk-e547.e4b6230b.css
  57. +1
    -0
      priv/static/adminfe/chunk-elementUI.e5cd8da6.css
  58. +0
    -1
      priv/static/adminfe/chunk-elementUI.f74c256b.css
  59. +1
    -1
      priv/static/adminfe/index.html
  60. BIN
      priv/static/adminfe/static/fonts/element-icons.2fad952.woff
  61. BIN
      priv/static/adminfe/static/fonts/element-icons.535877f.woff
  62. BIN
      priv/static/adminfe/static/fonts/element-icons.6f0a763.ttf
  63. BIN
      priv/static/adminfe/static/fonts/element-icons.732389d.ttf
  64. +0
    -1
      priv/static/adminfe/static/js/app.4137ad8f.js
  65. +1
    -0
      priv/static/adminfe/static/js/app.8e186193.js
  66. +1
    -0
      priv/static/adminfe/static/js/chunk-0e18.208cd826.js
  67. +1
    -0
      priv/static/adminfe/static/js/chunk-1fbf.616fb309.js
  68. +1
    -1
      priv/static/adminfe/static/js/chunk-2325.154a537b.js
  69. +0
    -1
      priv/static/adminfe/static/js/chunk-56c9.28e35fc3.js
  70. +1
    -0
      priv/static/adminfe/static/js/chunk-5e57.7313703a.js
  71. +0
    -1
      priv/static/adminfe/static/js/chunk-5eaf.5b76e416.js
  72. +1
    -0
      priv/static/adminfe/static/js/chunk-7fe2.458f9da5.js
  73. +1
    -0
      priv/static/adminfe/static/js/chunk-e547.d57d1b91.js
  74. +1
    -0
      priv/static/adminfe/static/js/chunk-elementUI.1911151b.js
  75. +0
    -1
      priv/static/adminfe/static/js/chunk-elementUI.1fa5434b.js
  76. +7
    -7
      priv/static/adminfe/static/js/chunk-libs.fb0b7f4a.js
  77. +0
    -1
      priv/static/adminfe/static/js/runtime.d8d12c12.js
  78. +1
    -0
      priv/static/adminfe/static/js/runtime.f40c8ec4.js
  79. +2
    -2
      test/config/transfer_task_test.exs
  80. +13
    -0
      test/support/mrf_module_mock.ex
  81. +36
    -0
      test/tasks/database_test.exs
  82. +32
    -0
      test/uploaders/local_test.exs
  83. +50
    -0
      test/uploaders/mdii_test.exs
  84. +90
    -0
      test/uploaders/s3_test.exs
  85. +4
    -1
      test/user_test.exs
  86. +45
    -5
      test/web/activity_pub/mrf/mrf_test.exs
  87. +123
    -0
      test/web/activity_pub/mrf/vocabulary_policy_test.exs
  88. +61
    -1
      test/web/activity_pub/relay_test.exs
  89. +22
    -8
      test/web/activity_pub/transmogrifier_test.exs
  90. +27
    -0
      test/web/mastodon_api/account_view_test.exs
  91. +114
    -31
      test/web/mastodon_api/mastodon_api_controller_test.exs
  92. +103
    -0
      test/web/mastodon_api/mastodon_api_test.exs
  93. +8
    -0
      test/web/node_info_test.exs

+ 13
- 0
CHANGELOG.md View File

@@ -23,9 +23,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Not being able to pin unlisted posts
- Objects being re-embedded to activities after being updated (e.g faved/reposted). Running 'mix pleroma.database prune_objects' again is advised.
- Metadata rendering errors resulting in the entire page being inaccessible
- `federation_incoming_replies_max_depth` option being ignored in certain cases
- Federation/MediaProxy not working with instances that have wrong certificate order
- Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`)
- Mastodon API: Embedded relationships not being properly rendered in the Account entity of Status entity
- Mastodon API: follower/following counters not being nullified, when `hide_follows`/`hide_followers` is set
- Mastodon API: `muted` in the Status entity, using author's account to determine if the tread was muted
- Mastodon API: Add `account_id`, `type`, `offset`, and `limit` to search API (`/api/v1/search` and `/api/v2/search`)
- Mastodon API, streaming: Fix filtering of notifications based on blocks/mutes/thread mutes
- ActivityPub C2S: follower/following collection pages being inaccessible even when authentifucated if `hide_followers`/ `hide_follows` was set
@@ -34,16 +37,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Rich Media: The crawled URL is now spliced into the rich media data.
- ActivityPub S2S: sharedInbox usage has been mostly aligned with the rules in the AP specification.
- ActivityPub S2S: remote user deletions now work the same as local user deletions.
- ActivityPub S2S: POST requests are now signed with `(request-target)` pseudo-header.
- Not being able to access the Mastodon FE login page on private instances
- Invalid SemVer version generation, when the current branch does not have commits ahead of tag/checked out on a tag
- Pleroma.Upload base_url was not automatically whitelisted by MediaProxy. Now your custom CDN or file hosting will be accessed directly as expected.
- Report email not being sent to admins when the reporter is a remote user
- MRF: ensure that subdomain_match calls are case-insensitive
- MRF: fix use of unserializable keyword lists in describe() implementations
- ActivityPub: Deactivated user deletion

### Added
- **Breaking:** MRF describe API, which adds support for exposing configuration information about MRF policies to NodeInfo.
Custom modules will need to be updated by adding, at the very least, `def describe, do: {:ok, %{}}` to the MRF policy modules.
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
- MRF: Support for excluding specific domains from Transparency.
- MRF: Support for filtering posts based on who they mention (`Pleroma.Web.ActivityPub.MRF.MentionPolicy`)
- MRF: Support for filtering posts based on ActivityStreams vocabulary (`Pleroma.Web.ActivityPub.MRF.VocabularyPolicy`)
- MRF (Simple Policy): Support for wildcard domains.
- Support for wildcard domains in user domain blocks setting.
- Configuration: `quarantined_instances` support wildcard domains.
@@ -66,6 +75,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Added synchronization of following/followers counters for external users
- Configuration: `enabled` option for `Pleroma.Emails.Mailer`, defaulting to `false`.
- Configuration: Pleroma.Plugs.RateLimiter `bucket_name`, `params` options.
- Configuration: `user_bio_length` and `user_name_length` options.
- Addressable lists
- Twitter API: added rate limit for `/api/account/password_reset` endpoint.
- ActivityPub: Add an internal service actor for fetching ActivityPub objects.
@@ -73,6 +83,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Admin API: Endpoint for fetching latest user's statuses
- Pleroma API: Add `/api/v1/pleroma/accounts/confirmation_resend?email=<email>` for resending account confirmation.
- Relays: Added a task to list relay subscriptions.
- Mix Tasks: `mix pleroma.database fix_likes_collections`
- Federation: Remove `likes` from objects.

### Changed
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
@@ -83,6 +95,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Removed
- Emoji: Remove longfox emojis.
- Remove `Reply-To` header from report emails for admins.
- ActivityPub: The `accept_blocks` configuration setting.

## [1.0.1] - 2019-07-14
### Security


+ 6
- 1
config/config.exs View File

@@ -253,6 +253,8 @@ config :pleroma, :instance,
skip_thread_containment: true,
limit_to_local_content: :unauthenticated,
dynamic_configuration: false,
user_bio_length: 5000,
user_name_length: 100,
external_user_synchronization: true

config :pleroma, :markup,
@@ -302,7 +304,6 @@ config :pleroma, :assets,
default_mascot: :pleroma_fox_tan

config :pleroma, :activitypub,
accept_blocks: true,
unfollow_blocked: true,
outgoing_blocks: true,
follow_handshake_timeout: 500,
@@ -337,6 +338,10 @@ config :pleroma, :mrf_keyword,

config :pleroma, :mrf_subchain, match_actor: %{}

config :pleroma, :mrf_vocabulary,
accept: [],
reject: []

config :pleroma, :rich_media,
enabled: true,
ignore_hosts: [],


+ 8
- 1
docs/config.md View File

@@ -18,6 +18,7 @@ Note: `strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`.

## Pleroma.Uploaders.S3
* `bucket`: S3 bucket name
* `bucket_namespace`: S3 bucket namespace
* `public_endpoint`: S3 endpoint that the user finally accesses(ex. "https://s3.dualstack.ap-northeast-1.amazonaws.com")
* `truncated_namespace`: If you use S3 compatible service such as Digital Ocean Spaces or CDN, set folder name or "" etc.
For example, when using CDN to S3 virtual host format, set "".
@@ -102,6 +103,7 @@ config :pleroma, Pleroma.Emails.Mailer,
* `Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy`: Rejects posts from likely spambots by rejecting posts from new users that contain links.
* `Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`: Crawls attachments using their MediaProxy URLs so that the MediaProxy cache is primed.
* `Pleroma.Web.ActivityPub.MRF.MentionPolicy`: Drops posts mentioning configurable users. (see `:mrf_mention` section)
* `Pleroma.Web.ActivityPub.MRF.VocabularyPolicy`: Restricts activities to a configured set of vocabulary. (see `:mrf_vocabulary` section)
* `public`: Makes the client API in authentificated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network.
* `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send.
* `managed_config`: Whenether the config for pleroma-fe is configured in this config or in ``static/config.json``
@@ -125,6 +127,8 @@ config :pleroma, Pleroma.Emails.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`.
* `healthcheck`: If set to true, system data will be shown on ``/api/pleroma/healthcheck``.
* `remote_post_retention_days`: The default amount of days to retain remote posts when pruning the database.
* `user_bio_length`: A user bio maximum length (default: `5000`)
* `user_name_length`: A user name maximum length (default: `100`)
* `skip_thread_containment`: Skip filter out broken threads. The default is `false`.
* `limit_to_local_content`: Limit unauthenticated users to search for local statutes and users only. Possible values: `:unauthenticated`, `:all` and `false`. The default is `:unauthenticated`.
* `dynamic_configuration`: Allow transferring configuration to DB with the subsequent customization from Admin api.
@@ -275,6 +279,10 @@ config :pleroma, :mrf_subchain,
## :mrf_mention
* `actors`: A list of actors, for which to drop any posts mentioning.

## :mrf_vocabulary
* `accept`: A list of ActivityStreams terms to accept. If empty, all supported messages are accepted.
* `reject`: A list of ActivityStreams terms to reject. If empty, no messages are rejected.

## :media_proxy
* `enabled`: Enables proxying of remote media to the instance’s proxy
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.
@@ -328,7 +336,6 @@ config :pleroma, Pleroma.Web.Endpoint,
This will make Pleroma listen on `127.0.0.1` port `8080` and generate urls starting with `https://example.com:2020`

## :activitypub
* ``accept_blocks``: Whether to accept incoming block activities from other instances
* ``unfollow_blocked``: Whether blocks result in people getting unfollowed
* ``outgoing_blocks``: Whether to federate blocks to other instances
* ``deny_follow_blocked``: Whether to disallow following an account that has blocked the user in question


+ 36
- 0
lib/mix/tasks/pleroma/database.ex View File

@@ -36,6 +36,10 @@ defmodule Mix.Tasks.Pleroma.Database do
## Remove duplicated items from following and update followers count for all users

mix pleroma.database update_users_following_followers_counts

## Fix the pre-existing "likes" collections for all objects

mix pleroma.database fix_likes_collections
"""
def run(["remove_embedded_objects" | args]) do
{options, [], []} =
@@ -125,4 +129,36 @@ defmodule Mix.Tasks.Pleroma.Database do
)
end
end

def run(["fix_likes_collections"]) do
import Ecto.Query

start_pleroma()

from(object in Object,
where: fragment("(?)->>'likes' is not null", object.data),
select: %{id: object.id, likes: fragment("(?)->>'likes'", object.data)}
)
|> Pleroma.RepoStreamer.chunk_stream(100)
|> Stream.each(fn objects ->
ids =
objects
|> Enum.filter(fn object -> object.likes |> Jason.decode!() |> is_map() end)
|> Enum.map(& &1.id)

Object
|> where([object], object.id in ^ids)
|> update([object],
set: [
data:
fragment(
"jsonb_set(?, '{likes}', '[]'::jsonb, true)",
object.data
)
]
)
|> Repo.update_all([], timeout: :infinity)
end)
|> Stream.run()
end
end

+ 25
- 2
lib/pleroma/activity.ex View File

@@ -224,6 +224,29 @@ defmodule Pleroma.Activity do

def get_create_by_object_ap_id(_), do: nil

def create_by_object_ap_id_with_object(ap_ids) when is_list(ap_ids) do
from(
activity in Activity,
where:
fragment(
"coalesce((?)->'object'->>'id', (?)->>'object') = ANY(?)",
activity.data,
activity.data,
^ap_ids
),
where: fragment("(?)->>'type' = 'Create'", activity.data),
inner_join: o in Object,
on:
fragment(
"(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')",
o.data,
activity.data,
activity.data
),
preload: [object: o]
)
end

def create_by_object_ap_id_with_object(ap_id) when is_binary(ap_id) do
from(
activity in Activity,
@@ -263,8 +286,8 @@ defmodule Pleroma.Activity do

defp get_in_reply_to_activity_from_object(_), do: nil

def get_in_reply_to_activity(%Activity{data: %{"object" => object}}) do
get_in_reply_to_activity_from_object(Object.normalize(object))
def get_in_reply_to_activity(%Activity{} = activity) do
get_in_reply_to_activity_from_object(Object.normalize(activity))
end

def normalize(obj) when is_map(obj), do: get_by_ap_id_with_object(obj["id"])


+ 63
- 129
lib/pleroma/application.ex View File

@@ -3,11 +3,14 @@
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Application do
import Cachex.Spec
use Application

@name Mix.Project.config()[:name]
@version Mix.Project.config()[:version]
@repository Mix.Project.config()[:source_url]
@env Mix.env()

def name, do: @name
def version, do: @version
def named_version, do: @name <> " " <> @version
@@ -21,116 +24,25 @@ defmodule Pleroma.Application do
# See http://elixir-lang.org/docs/stable/elixir/Application.html
# for more information on OTP Applications
def start(_type, _args) do
import Cachex.Spec

Pleroma.Config.DeprecationWarnings.warn()
setup_instrumenters()

# Define workers and child supervisors to be supervised
children =
[
# Start the Ecto repository
%{id: Pleroma.Repo, start: {Pleroma.Repo, :start_link, []}, type: :supervisor},
%{id: Pleroma.Config.TransferTask, start: {Pleroma.Config.TransferTask, :start_link, []}},
%{id: Pleroma.Emoji, start: {Pleroma.Emoji, :start_link, []}},
%{id: Pleroma.Captcha, start: {Pleroma.Captcha, :start_link, []}},
%{
id: :cachex_used_captcha_cache,
start:
{Cachex, :start_link,
[
:used_captcha_cache,
[
ttl_interval:
:timer.seconds(Pleroma.Config.get!([Pleroma.Captcha, :seconds_valid]))
]
]}
},
%{
id: :cachex_user,
start:
{Cachex, :start_link,
[
:user_cache,
[
default_ttl: 25_000,
ttl_interval: 1000,
limit: 2500
]
]}
},
%{
id: :cachex_object,
start:
{Cachex, :start_link,
[
:object_cache,
[
default_ttl: 25_000,
ttl_interval: 1000,
limit: 2500
]
]}
},
%{
id: :cachex_rich_media,
start:
{Cachex, :start_link,
[
:rich_media_cache,
[
default_ttl: :timer.minutes(120),
limit: 5000
]
]}
},
%{
id: :cachex_scrubber,
start:
{Cachex, :start_link,
[
:scrubber_cache,
[
limit: 2500
]
]}
},
%{
id: :cachex_idem,
start:
{Cachex, :start_link,
[
:idempotency_cache,
[
expiration:
expiration(
default: :timer.seconds(6 * 60 * 60),
interval: :timer.seconds(60)
),
limit: 2500
]
]}
},
%{id: Pleroma.FlakeId, start: {Pleroma.FlakeId, :start_link, []}},
%{
id: Pleroma.ScheduledActivityWorker,
start: {Pleroma.ScheduledActivityWorker, :start_link, []}
}
Pleroma.Repo,
Pleroma.Config.TransferTask,
Pleroma.Emoji,
Pleroma.Captcha,
Pleroma.FlakeId,
Pleroma.ScheduledActivityWorker
] ++
cachex_children() ++
hackney_pool_children() ++
[
%{
id: Pleroma.Web.Federator.RetryQueue,
start: {Pleroma.Web.Federator.RetryQueue, :start_link, []}
},
%{
id: Pleroma.Web.OAuth.Token.CleanWorker,
start: {Pleroma.Web.OAuth.Token.CleanWorker, :start_link, []}
},
%{
id: Pleroma.Stats,
start: {Pleroma.Stats, :start_link, []}
},
Pleroma.Web.Federator.RetryQueue,
Pleroma.Web.OAuth.Token.CleanWorker,
Pleroma.Stats,
%{
id: :web_push_init,
start: {Task, :start_link, [&Pleroma.Web.Push.init/0]},
@@ -147,16 +59,12 @@ defmodule Pleroma.Application do
restart: :temporary
}
] ++
streamer_child() ++
chat_child() ++
oauth_cleanup_child(oauth_cleanup_enabled?()) ++
streamer_child(@env) ++
chat_child(@env, chat_enabled?()) ++
[
# Start the endpoint when the application starts
%{
id: Pleroma.Web.Endpoint,
start: {Pleroma.Web.Endpoint, :start_link, []},
type: :supervisor
},
%{id: Pleroma.Gopher.Server, start: {Pleroma.Gopher.Server, :start_link, []}}
Pleroma.Web.Endpoint,
Pleroma.Gopher.Server
]

# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
@@ -201,28 +109,54 @@ defmodule Pleroma.Application do
end
end

if Pleroma.Config.get(:env) == :test do
defp streamer_child, do: []
defp chat_child, do: []
else
defp streamer_child do
[%{id: Pleroma.Web.Streamer, start: {Pleroma.Web.Streamer, :start_link, []}}]
end
defp cachex_children do
[
build_cachex("used_captcha", ttl_interval: seconds_valid_interval()),
build_cachex("user", default_ttl: 25_000, ttl_interval: 1000, limit: 2500),
build_cachex("object", default_ttl: 25_000, ttl_interval: 1000, limit: 2500),
build_cachex("rich_media", default_ttl: :timer.minutes(120), limit: 5000),
build_cachex("scrubber", limit: 2500),
build_cachex("idempotency", expiration: idempotency_expiration(), limit: 2500)
]
end

defp chat_child do
if Pleroma.Config.get([:chat, :enabled]) do
[
%{
id: Pleroma.Web.ChatChannel.ChatChannelState,
start: {Pleroma.Web.ChatChannel.ChatChannelState, :start_link, []}
}
]
else
[]
end
end
defp idempotency_expiration,
do: expiration(default: :timer.seconds(6 * 60 * 60), interval: :timer.seconds(60))

defp seconds_valid_interval,
do: :timer.seconds(Pleroma.Config.get!([Pleroma.Captcha, :seconds_valid]))

defp build_cachex(type, opts),
do: %{
id: String.to_atom("cachex_" <> type),
start: {Cachex, :start_link, [String.to_atom(type <> "_cache"), opts]},
type: :worker
}

defp chat_enabled?, do: Pleroma.Config.get([:chat, :enabled])

defp oauth_cleanup_enabled?,
do: Pleroma.Config.get([:oauth2, :clean_expired_tokens], false)

defp streamer_child(:test), do: []

defp streamer_child(_) do
[Pleroma.Web.Streamer]
end

defp oauth_cleanup_child(true),
do: [Pleroma.Web.OAuth.Token.CleanWorker]

defp oauth_cleanup_child(_), do: []

defp chat_child(:test, _), do: []

defp chat_child(_env, true) do
[Pleroma.Web.ChatChannel.ChatChannelState]
end

defp chat_child(_, _), do: []

defp hackney_pool_children do
for pool <- enabled_hackney_pools() do
options = Pleroma.Config.get([:hackney_pools, pool])


+ 1
- 1
lib/pleroma/captcha/captcha.ex View File

@@ -12,7 +12,7 @@ defmodule Pleroma.Captcha do
use GenServer

@doc false
def start_link do
def start_link(_) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end



+ 1
- 1
lib/pleroma/config/transfer_task.ex View File

@@ -6,7 +6,7 @@ defmodule Pleroma.Config.TransferTask do
use Task
alias Pleroma.Web.AdminAPI.Config

def start_link do
def start_link(_) do
load_and_update_env()
if Pleroma.Config.get(:env) == :test, do: Ecto.Adapters.SQL.Sandbox.checkin(Pleroma.Repo)
:ignore


+ 1
- 1
lib/pleroma/emoji.ex View File

@@ -24,7 +24,7 @@ defmodule Pleroma.Emoji do
@ets_options [:ordered_set, :protected, :named_table, {:read_concurrency, true}]

@doc false
def start_link do
def start_link(_) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end



+ 1
- 1
lib/pleroma/flake_id.ex View File

@@ -98,7 +98,7 @@ defmodule Pleroma.FlakeId do
def autogenerate, do: get()

# -- GenServer API
def start_link do
def start_link(_) do
:gen_server.start_link({:local, :flake}, __MODULE__, [], [])
end



+ 1
- 1
lib/pleroma/gopher/server.ex View File

@@ -6,7 +6,7 @@ defmodule Pleroma.Gopher.Server do
use GenServer
require Logger

def start_link do
def start_link(_) do
config = Pleroma.Config.get(:gopher, [])
ip = Keyword.get(config, :ip, {0, 0, 0, 0})
port = Keyword.get(config, :port, 1234)


+ 1
- 1
lib/pleroma/scheduled_activity_worker.ex View File

@@ -16,7 +16,7 @@ defmodule Pleroma.ScheduledActivityWorker do

@schedule_interval :timer.minutes(1)

def start_link do
def start_link(_) do
GenServer.start_link(__MODULE__, nil)
end



+ 43
- 17
lib/pleroma/stats.ex View File

@@ -7,31 +7,56 @@ defmodule Pleroma.Stats do
alias Pleroma.Repo
alias Pleroma.User

def start_link do
agent = Agent.start_link(fn -> {[], %{}} end, name: __MODULE__)
spawn(fn -> schedule_update() end)
agent
use GenServer

@interval 1000 * 60 * 60

def start_link(_) do
GenServer.start_link(__MODULE__, initial_data(), name: __MODULE__)
end

def force_update do
GenServer.call(__MODULE__, :force_update)
end

def get_stats do
Agent.get(__MODULE__, fn {_, stats} -> stats end)
%{stats: stats} = GenServer.call(__MODULE__, :get_state)

stats
end

def get_peers do
Agent.get(__MODULE__, fn {peers, _} -> peers end)
%{peers: peers} = GenServer.call(__MODULE__, :get_state)

peers
end

def init(args) do
Process.send(self(), :run_update, [])
{:ok, args}
end

def handle_call(:force_update, _from, _state) do
new_stats = get_stat_data()
{:reply, new_stats, new_stats}
end

def schedule_update do
spawn(fn ->
# 1 hour
Process.sleep(1000 * 60 * 60)
schedule_update()
end)
def handle_call(:get_state, _from, state) do
{:reply, state, state}
end

def handle_info(:run_update, _state) do
new_stats = get_stat_data()

Process.send_after(self(), :run_update, @interval)
{:noreply, new_stats}
end

update_stats()
defp initial_data do
%{peers: [], stats: %{}}
end

def update_stats do
defp get_stat_data do
peers =
from(
u in User,
@@ -52,8 +77,9 @@ defmodule Pleroma.Stats do

user_count = Repo.aggregate(User.Query.build(%{local: true, active: true}), :count, :id)

Agent.update(__MODULE__, fn _ ->
{peers, %{domain_count: domain_count, status_count: status_count, user_count: user_count}}
end)
%{
peers: peers,
stats: %{domain_count: domain_count, status_count: status_count, user_count: user_count}
}
end
end

+ 2
- 2
lib/pleroma/uploaders/local.ex View File

@@ -11,7 +11,7 @@ defmodule Pleroma.Uploaders.Local do

def put_file(upload) do
{local_path, file} =
case Enum.reverse(String.split(upload.path, "/", trim: true)) do
case Enum.reverse(Path.split(upload.path)) do
[file] ->
{upload_path(), file}

@@ -23,7 +23,7 @@ defmodule Pleroma.Uploaders.Local do

result_file = Path.join(local_path, file)

unless File.exists?(result_file) do
if not File.exists?(result_file) do
File.cp!(upload.tempfile, result_file)
end



+ 2
- 0
lib/pleroma/uploaders/mdii.ex View File

@@ -3,6 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Uploaders.MDII do
@moduledoc "Represents uploader for https://github.com/hakaba-hitoyo/minimal-digital-image-infrastructure"

alias Pleroma.Config
alias Pleroma.HTTP



+ 7
- 5
lib/pleroma/uploaders/s3.ex View File

@@ -6,10 +6,12 @@ defmodule Pleroma.Uploaders.S3 do
@behaviour Pleroma.Uploaders.Uploader
require Logger

alias Pleroma.Config

# The file name is re-encoded with S3's constraints here to comply with previous
# links with less strict filenames
def get_file(file) do
config = Pleroma.Config.get([__MODULE__])
config = Config.get([__MODULE__])
bucket = Keyword.fetch!(config, :bucket)

bucket_with_namespace =
@@ -34,15 +36,15 @@ defmodule Pleroma.Uploaders.S3 do
end

def put_file(%Pleroma.Upload{} = upload) do
config = Pleroma.Config.get([__MODULE__])
config = Config.get([__MODULE__])
bucket = Keyword.get(config, :bucket)

{:ok, file_data} = File.read(upload.tempfile)

s3_name = strict_encode(upload.path)

op =
ExAws.S3.put_object(bucket, s3_name, file_data, [
upload.tempfile
|> ExAws.S3.Upload.stream_file()
|> ExAws.S3.upload(bucket, s3_name, [
{:acl, :public_read},
{:content_type, upload.content_type}
])


+ 21
- 17
lib/pleroma/user.ex View File

@@ -152,10 +152,10 @@ defmodule Pleroma.User do
end

def remote_user_creation(params) do
params =
params
|> Map.put(:info, params[:info] || %{})
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)

params = Map.put(params, :info, params[:info] || %{})
info_cng = User.Info.remote_user_creation(%User.Info{}, params[:info])

changes =
@@ -164,8 +164,8 @@ defmodule Pleroma.User do
|> validate_required([:name, :ap_id])
|> unique_constraint(:nickname)
|> validate_format(:nickname, @email_regex)
|> validate_length(:bio, max: 5000)
|> validate_length(:name, max: 100)
|> validate_length(:bio, max: bio_limit)
|> validate_length(:name, max: name_limit)
|> put_change(:local, false)
|> put_embed(:info, info_cng)

@@ -188,22 +188,23 @@ defmodule Pleroma.User do
end

def update_changeset(struct, params \\ %{}) do
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)

struct
|> cast(params, [:bio, :name, :avatar, :following])
|> unique_constraint(:nickname)
|> validate_format(:nickname, local_nickname_regex())
|> validate_length(:bio, max: 5000)
|> validate_length(:name, min: 1, max: 100)
|> validate_length(:bio, max: bio_limit)
|> validate_length(:name, min: 1, max: name_limit)
end

def upgrade_changeset(struct, params \\ %{}) do
params =
params
|> Map.put(:last_refreshed_at, NaiveDateTime.utc_now())
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)

info_cng =
struct.info
|> User.Info.user_upgrade(params[:info])
params = Map.put(params, :last_refreshed_at, NaiveDateTime.utc_now())
info_cng = User.Info.user_upgrade(struct.info, params[:info])

struct
|> cast(params, [
@@ -216,8 +217,8 @@ defmodule Pleroma.User do
])
|> unique_constraint(:nickname)
|> validate_format(:nickname, local_nickname_regex())
|> validate_length(:bio, max: 5000)
|> validate_length(:name, max: 100)
|> validate_length(:bio, max: bio_limit)
|> validate_length(:name, max: name_limit)
|> put_embed(:info, info_cng)
end

@@ -244,6 +245,9 @@ defmodule Pleroma.User do
end

def register_changeset(struct, params \\ %{}, opts \\ []) do
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)

need_confirmation? =
if is_nil(opts[:need_confirmation]) do
Pleroma.Config.get([:instance, :account_activation_required])
@@ -264,8 +268,8 @@ defmodule Pleroma.User do
|> validate_exclusion(:nickname, Pleroma.Config.get([User, :restricted_nicknames]))
|> validate_format(:nickname, local_nickname_regex())
|> validate_format(:email, @email_regex)
|> validate_length(:bio, max: 1000)
|> validate_length(:name, min: 1, max: 100)
|> validate_length(:bio, max: bio_limit)
|> validate_length(:name, min: 1, max: name_limit)
|> put_change(:info, info_change)

changeset =


+ 10
- 0
lib/pleroma/web/activity_pub/activity_pub.ex View File

@@ -520,6 +520,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do

from(activity in Activity)
|> maybe_preload_objects(opts)
|> maybe_preload_bookmarks(opts)
|> maybe_set_thread_muted_field(opts)
|> restrict_blocked(opts)
|> restrict_recipients(recipients, opts["user"])
|> where(
@@ -533,6 +535,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
)
)
|> exclude_poll_votes(opts)
|> exclude_id(opts)
|> order_by([activity], desc: activity.id)
end

@@ -625,6 +628,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
params =
params
|> Map.put("type", ["Create", "Announce"])
|> Map.put("user", reading_user)
|> Map.put("actor_id", user.ap_id)
|> Map.put("whole_db", true)
|> Map.put("pinned_activity_ids", user.info.pinned_activities)
@@ -872,6 +876,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end

defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
from(activity in query, where: activity.id != ^id)
end

defp exclude_id(query, _), do: query

defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query

defp maybe_preload_objects(query, _) do


+ 33
- 1
lib/pleroma/web/activity_pub/mrf.ex View File

@@ -28,11 +28,43 @@ defmodule Pleroma.Web.ActivityPub.MRF do

@spec subdomains_regex([String.t()]) :: [Regex.t()]
def subdomains_regex(domains) when is_list(domains) do
for domain <- domains, do: ~r(^#{String.replace(domain, "*.", "(.*\\.)*")}$)
for domain <- domains, do: ~r(^#{String.replace(domain, "*.", "(.*\\.)*")}$)i
end

@spec subdomain_match?([Regex.t()], String.t()) :: boolean()
def subdomain_match?(domains, host) do
Enum.any?(domains, fn domain -> Regex.match?(domain, host) end)
end

@callback describe() :: {:ok | :error, Map.t()}

def describe(policies) do
{:ok, policy_configs} =
policies
|> Enum.reduce({:ok, %{}}, fn
policy, {:ok, data} ->
{:ok, policy_data} = policy.describe()
{:ok, Map.merge(data, policy_data)}

_, error ->
error
end)

mrf_policies =
get_policies()
|> Enum.map(fn policy -> to_string(policy) |> String.split(".") |> List.last() end)

exclusions = Pleroma.Config.get([:instance, :mrf_transparency_exclusions])

base =
%{
mrf_policies: mrf_policies,
exclusions: length(exclusions) > 0
}
|> Map.merge(policy_configs)

{:ok, base}
end

def describe, do: get_policies() |> describe()
end

+ 3
- 0
lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex View File

@@ -62,4 +62,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do

@impl true
def filter(message), do: {:ok, message}

@impl true
def describe, do: {:ok, %{}}
end

+ 6
- 0
lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex View File

@@ -5,6 +5,8 @@
defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do
alias Pleroma.User

@behaviour Pleroma.Web.ActivityPub.MRF

require Logger

# has the user successfully posted before?
@@ -22,6 +24,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do

defp contains_links?(_), do: false

@impl true
def filter(%{"type" => "Create", "actor" => actor, "object" => object} = message) do
with {:ok, %User{} = u} <- User.get_or_fetch_by_ap_id(actor),
{:contains_links, true} <- {:contains_links, contains_links?(object)},
@@ -45,4 +48,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do

# in all other cases, pass through
def filter(message), do: {:ok, message}

@impl true
def describe, do: {:ok, %{}}
end

+ 3
- 0
lib/pleroma/web/activity_pub/mrf/drop_policy.ex View File

@@ -12,4 +12,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
Logger.info("REJECTING #{inspect(object)}")
{:reject, object}
end

@impl true
def describe, do: {:ok, %{}}
end

+ 2
- 0
lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex View File

@@ -39,4 +39,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
end

def filter(object), do: {:ok, object}

def describe, do: {:ok, %{}}
end

+ 4
- 0
lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex View File

@@ -90,4 +90,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do

@impl true
def filter(message), do: {:ok, message}

@impl true
def describe,
do: {:ok, %{mrf_hellthread: Pleroma.Config.get(:mrf_hellthread) |> Enum.into(%{})}}
end

+ 32
- 0
lib/pleroma/web/activity_pub/mrf/keyword_policy.ex View File

@@ -96,4 +96,36 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do

@impl true
def filter(message), do: {:ok, message}

@impl true
def describe do
# This horror is needed to convert regex sigils to strings
mrf_keyword =
Pleroma.Config.get(:mrf_keyword, [])
|> Enum.map(fn {key, value} ->
{key,
Enum.map(value, fn
{pattern, replacement} ->
%{
"pattern" =>
if not is_binary(pattern) do
inspect(pattern)
else
pattern
end,
"replacement" => replacement
}

pattern ->
if not is_binary(pattern) do
inspect(pattern)
else
pattern
end
end)}
end)
|> Enum.into(%{})

{:ok, %{mrf_keyword: mrf_keyword}}
end
end

+ 3
- 0
lib/pleroma/web/activity_pub/mrf/mediaproxy_warming_policy.ex View File

@@ -53,4 +53,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do

@impl true
def filter(message), do: {:ok, message}

@impl true
def describe, do: {:ok, %{}}
end

+ 3
- 0
lib/pleroma/web/activity_pub/mrf/mention_policy.ex View File

@@ -21,4 +21,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do

@impl true
def filter(message), do: {:ok, message}

@impl true
def describe, do: {:ok, %{}}
end

+ 3
- 0
lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex View File

@@ -19,4 +19,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do

@impl true
def filter(object), do: {:ok, object}

@impl true
def describe, do: {:ok, %{}}
end

+ 3
- 0
lib/pleroma/web/activity_pub/mrf/noop_policy.ex View File

@@ -10,4 +10,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
def filter(object) do
{:ok, object}
end

@impl true
def describe, do: {:ok, %{}}
end

+ 2
- 0
lib/pleroma/web/activity_pub/mrf/normalize_markup.ex View File

@@ -21,4 +21,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
end

def filter(object), do: {:ok, object}

def describe, do: {:ok, %{}}
end

+ 4
- 0
lib/pleroma/web/activity_pub/mrf/reject_non_public.ex View File

@@ -44,4 +44,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do

@impl true
def filter(object), do: {:ok, object}

@impl true
def describe,
do: {:ok, %{mrf_rejectnonpublic: Pleroma.Config.get(:mrf_rejectnonpublic) |> Enum.into(%{})}}
end

+ 12
- 0
lib/pleroma/web/activity_pub/mrf/simple_policy.ex View File

@@ -177,4 +177,16 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
end

def filter(object), do: {:ok, object}

@impl true
def describe do
exclusions = Pleroma.Config.get([:instance, :mrf_transparency_exclusions])

mrf_simple =
Pleroma.Config.get(:mrf_simple)
|> Enum.map(fn {k, v} -> {k, Enum.reject(v, fn v -> v in exclusions end)} end)
|> Enum.into(%{})

{:ok, %{mrf_simple: mrf_simple}}
end
end

+ 3
- 0
lib/pleroma/web/activity_pub/mrf/subchain_policy.ex View File

@@ -37,4 +37,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do

@impl true
def filter(message), do: {:ok, message}

@impl true
def describe, do: {:ok, %{}}
end

+ 3
- 0
lib/pleroma/web/activity_pub/mrf/tag_policy.ex View File

@@ -165,4 +165,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do

@impl true
def filter(message), do: {:ok, message}

@impl true
def describe, do: {:ok, %{}}
end

+ 9
- 0
lib/pleroma/web/activity_pub/mrf/user_allowlist_policy.ex View File

@@ -32,4 +32,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
end

def filter(object), do: {:ok, object}

@impl true
def describe do
mrf_user_allowlist =
Config.get([:mrf_user_allowlist], [])
|> Enum.into(%{}, fn {k, v} -> {k, length(v)} end)

{:ok, %{mrf_user_allowlist: mrf_user_allowlist}}
end
end

+ 37
- 0
lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex View File

@@ -0,0 +1,37 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
@moduledoc "Filter messages which belong to certain activity vocabularies"

@behaviour Pleroma.Web.ActivityPub.MRF

def filter(%{"type" => "Undo", "object" => child_message} = message) do
with {:ok, _} <- filter(child_message) do
{:ok, message}
else
{:reject, nil} ->
{:reject, nil}
end
end

def filter(%{"type" => message_type} = message) do
with accepted_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :accept]),
rejected_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :reject]),
true <-
length(accepted_vocabulary) == 0 || Enum.member?(accepted_vocabulary, message_type),
false <-
length(rejected_vocabulary) > 0 && Enum.member?(rejected_vocabulary, message_type),
{:ok, _} <- filter(message["object"]) do
{:ok, message}
else
_ -> {:reject, nil}
end
end

def filter(message), do: {:ok, message}

def describe,
do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary) |> Enum.into(%{})}}
end

+ 2
- 1
lib/pleroma/web/activity_pub/publisher.ex View File

@@ -46,7 +46,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
"""
def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = params) do
Logger.info("Federating #{id} to #{inbox}")
host = URI.parse(inbox).host
%{host: host, path: path} = URI.parse(inbox)

digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())

@@ -56,6 +56,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do

signature =
Pleroma.Signature.sign(actor, %{
"(request-target)": "post #{path}",
host: host,
"content-length": byte_size(json),
digest: digest,


+ 15
- 2
lib/pleroma/web/activity_pub/relay.ex View File

@@ -14,6 +14,7 @@ defmodule Pleroma.Web.ActivityPub.Relay do
|> User.get_or_create_service_actor_by_ap_id()
end

@spec follow(String.t()) :: {:ok, Activity.t()} | {:error, any()}
def follow(target_instance) do
with %User{} = local_user <- get_actor(),
{:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_instance),
@@ -21,12 +22,17 @@ defmodule Pleroma.Web.ActivityPub.Relay do
Logger.info("relay: followed instance: #{target_instance}; id=#{activity.data["id"]}")
{:ok, activity}
else
{:error, _} = error ->
Logger.error("error: #{inspect(error)}")
error

e ->
Logger.error("error: #{inspect(e)}")
{:error, e}
end
end

@spec unfollow(String.t()) :: {:ok, Activity.t()} | {:error, any()}
def unfollow(target_instance) do
with %User{} = local_user <- get_actor(),
{:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_instance),
@@ -34,20 +40,27 @@ defmodule Pleroma.Web.ActivityPub.Relay do
Logger.info("relay: unfollowed instance: #{target_instance}: id=#{activity.data["id"]}")
{:ok, activity}
else
{:error, _} = error ->
Logger.error("error: #{inspect(error)}")
error

e ->
Logger.error("error: #{inspect(e)}")
{:error, e}
end
end

@spec publish(any()) :: {:ok, Activity.t(), Object.t()} | {:error, any()}
def publish(%Activity{data: %{"type" => "Create"}} = activity) do
with %User{} = user <- get_actor(),
%Object{} = object <- Object.normalize(activity) do
ActivityPub.announce(user, object, nil, true, false)
else
e -> Logger.error("error: #{inspect(e)}")
e ->
Logger.error("error: #{inspect(e)}")
{:error, inspect(e)}
end
end

def publish(_), do: nil
def publish(_), do: {:error, "Not implemented"}
end

+ 10
- 40
lib/pleroma/web/activity_pub/transmogrifier.ex View File

@@ -26,6 +26,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
"""
def fix_object(object, options \\ []) do
object
|> strip_internal_fields
|> fix_actor
|> fix_url
|> fix_attachments
@@ -34,7 +35,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> fix_emoji
|> fix_tag
|> fix_content_map
|> fix_likes
|> fix_addressing
|> fix_summary
|> fix_type(options)
@@ -151,20 +151,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> Map.put("actor", Containment.get_actor(%{"actor" => actor}))
end

# Check for standardisation
# This is what Peertube does
# curl -H 'Accept: application/activity+json' $likes | jq .totalItems
# Prismo returns only an integer (count) as "likes"
def fix_likes(%{"likes" => likes} = object) when not is_map(likes) do
object
|> Map.put("likes", [])
|> Map.put("like_count", 0)
end

def fix_likes(object) do
object
end

def fix_in_reply_to(object, options \\ [])

def fix_in_reply_to(%{"inReplyTo" => in_reply_to} = object, options)
@@ -347,13 +333,15 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do

def fix_type(object, options \\ [])

def fix_type(%{"inReplyTo" => reply_id} = object, options) when is_binary(reply_id) do
def fix_type(%{"inReplyTo" => reply_id, "name" => _} = object, options)
when is_binary(reply_id) do
reply =
if Federator.allowed_incoming_reply_depth?(options[:depth]) do
Object.normalize(reply_id, true)
with true <- Federator.allowed_incoming_reply_depth?(options[:depth]),
{:ok, object} <- get_obj_helper(reply_id, options) do
object
end

if reply && (reply.data["type"] == "Question" and object["name"]) do
if reply && reply.data["type"] == "Question" do
Map.put(object, "type", "Answer")
else
object
@@ -713,8 +701,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
} = _data,
_options
) do
with true <- Pleroma.Config.get([:activitypub, :accept_blocks]),
%User{local: true} = blocked <- User.get_cached_by_ap_id(blocked),
with %User{local: true} = blocked <- User.get_cached_by_ap_id(blocked),
{:ok, %User{} = blocker} <- User.get_or_fetch_by_ap_id(blocker),
{:ok, activity} <- ActivityPub.unblock(blocker, blocked, id, false) do
User.unblock(blocker, blocked)
@@ -728,8 +715,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
%{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = _data,
_options
) do
with true <- Pleroma.Config.get([:activitypub, :accept_blocks]),
%User{local: true} = blocked = User.get_cached_by_ap_id(blocked),
with %User{local: true} = blocked = User.get_cached_by_ap_id(blocked),
{:ok, %User{} = blocker} = User.get_or_fetch_by_ap_id(blocker),
{:ok, activity} <- ActivityPub.block(blocker, blocked, id, false) do
User.unfollow(blocker, blocked)
@@ -784,7 +770,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> add_mention_tags
|> add_emoji_tags
|> add_attributed_to
|> add_likes
|> prepare_attachments
|> set_conversation
|> set_reply_to_uri
@@ -971,22 +956,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> Map.put("attributedTo", attributed_to)
end

def add_likes(%{"id" => id, "like_count" => likes} = object) do
likes = %{
"id" => "#{id}/likes",
"first" => "#{id}/likes?page=1",
"type" => "OrderedCollection",
"totalItems" => likes
}

object
|> Map.put("likes", likes)
end

def add_likes(object) do
object
end

def prepare_attachments(object) do
attachments =
(object["attachment"] || [])
@@ -1002,6 +971,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
defp strip_internal_fields(object) do
object
|> Map.drop([
"likes",
"like_count",
"announcements",
"announcement_count",


+ 3
- 1
lib/pleroma/web/chat_channel.ex View File

@@ -33,9 +33,11 @@ defmodule Pleroma.Web.ChatChannel do
end

defmodule Pleroma.Web.ChatChannel.ChatChannelState do
use Agent

@max_messages 20

def start_link do
def start_link(_) do
Agent.start_link(fn -> %{max_id: 1, messages: []} end, name: __MODULE__)
end



+ 1
- 1
lib/pleroma/web/federator/retry_queue.ex View File

@@ -13,7 +13,7 @@ defmodule Pleroma.Web.Federator.RetryQueue do
{:ok, %{args | queue_table: queue_table, running_jobs: :sets.new()}}
end

def start_link do
def start_link(_) do
enabled =
if Pleroma.Config.get(:env) == :test,
do: true,


+ 14
- 10
lib/pleroma/web/mastodon_api/mastodon_api.ex View File

@@ -13,10 +13,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
alias Pleroma.User
alias Pleroma.Web.CommonAPI

@spec follow(User.t(), User.t(), map) :: {:ok, User.t()} | {:error, String.t()}
def follow(follower, followed, params \\ %{}) do
options = cast_params(params)
reblogs = options[:reblogs]

result =
if not User.following?(follower, followed) do
CommonAPI.follow(follower, followed)
@@ -24,19 +22,25 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
{:ok, follower, followed, nil}
end

with {:ok, follower, followed, _} <- result do
reblogs
|> case do
false -> CommonAPI.hide_reblogs(follower, followed)
_ -> CommonAPI.show_reblogs(follower, followed)
end
|> case do
with {:ok, follower, _followed, _} <- result do
options = cast_params(params)

case reblogs_visibility(options[:reblogs], result) do
{:ok, follower} -> {:ok, follower}
_ -> {:ok, follower}
end
end
end

defp reblogs_visibility(false, {:ok, follower, followed, _}) do
CommonAPI.hide_reblogs(follower, followed)
end

defp reblogs_visibility(_, {:ok, follower, followed, _}) do
CommonAPI.show_reblogs(follower, followed)
end

@spec get_followers(User.t(), map()) :: list(User.t())
def get_followers(user, params \\ %{}) do
user
|> User.get_followers_query()


+ 26
- 36
lib/pleroma/web/mastodon_api/mastodon_api_controller.ex View File

@@ -435,6 +435,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> Map.put("local_only", local_only)
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("user", user)
|> ActivityPub.fetch_public_activities()
|> Enum.reverse()

@@ -496,12 +497,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
activities <-
ActivityPub.fetch_activities_for_context(activity.data["context"], %{
"blocking_user" => user,
"user" => user
"user" => user,
"exclude_id" => activity.id
}),
activities <-
activities |> Enum.filter(fn %{id: aid} -> to_string(aid) != to_string(id) end),
activities <-
activities |> Enum.filter(fn %{data: %{"type" => type}} -> type == "Create" end),
grouped_activities <- Enum.group_by(activities, fn %{id: id} -> id < activity.id end) do
result = %{
ancestors:
@@ -536,8 +534,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> put_view(StatusView)
|> try_render("poll.json", %{object: object, for: user})
else
nil -> render_error(conn, :not_found, "Record not found")
false -> render_error(conn, :not_found, "Record not found")
error when is_nil(error) or error == false ->
render_error(conn, :not_found, "Record not found")
end
end

@@ -885,8 +883,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end

def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Activity{data: %{"object" => object}} <- Activity.get_by_id(id),
%Object{data: %{"likes" => likes}} <- Object.normalize(object) do
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
%Object{data: %{"likes" => likes}} <- Object.normalize(activity) do
q = from(u in User, where: u.ap_id in ^likes)

users =
@@ -902,8 +900,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end

def reblogged_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Activity{data: %{"object" => object}} <- Activity.get_by_id(id),
%Object{data: %{"announcements" => announces}} <- Object.normalize(object) do
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
%Object{data: %{"announcements" => announces}} <- Object.normalize(activity) do
q = from(u in User, where: u.ap_id in ^announces)

users =
@@ -944,6 +942,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> Map.put("local_only", local_only)
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("user", user)
|> Map.put("tag", tags)
|> Map.put("tag_all", tag_all)
|> Map.put("tag_reject", tag_reject)
@@ -1350,6 +1349,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
params
|> Map.put("type", "Create")
|> Map.put("blocking_user", user)
|> Map.put("user", user)
|> Map.put("muting_user", user)

# we must filter the following list for the user to avoid leaking statuses the user
@@ -1690,45 +1690,35 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> String.replace("{{user}}", user)

with {:ok, %{status: 200, body: body}} <-
HTTP.get(
url,
[],
adapter: [
recv_timeout: timeout,
pool: :default
]
),
HTTP.get(url, [], adapter: [recv_timeout: timeout, pool: :default]),
{:ok, data} <- Jason.decode(body) do
data =
data
|> Enum.slice(0, limit)
|> Enum.map(fn x ->
Map.put(
x,
"id",
case User.get_or_fetch(x["acct"]) do
{:ok, %User{id: id}} -> id
_ -> 0
end
)
end)
|> Enum.map(fn x ->
Map.put(x, "avatar", MediaProxy.url(x["avatar"]))
end)
|> Enum.map(fn x ->
Map.put(x, "avatar_static", MediaProxy.url(x["avatar_static"]))
x
|> Map.put("id", fetch_suggestion_id(x))
|> Map.put("avatar", MediaProxy.url(x["avatar"]))
|> Map.put("avatar_static", MediaProxy.url(x["avatar_static"]))
end)

conn
|> json(data)
json(conn, data)
else
e -> Logger.error("Could not retrieve suggestions at fetch #{url}, #{inspect(e)}")
e ->
Logger.error("Could not retrieve suggestions at fetch #{url}, #{inspect(e)}")
end
else
json(conn, [])
end
end

defp fetch_suggestion_id(attrs) do
case User.get_or_fetch(attrs["acct"]) do
{:ok, %User{id: id}} -> id
_ -> 0
end
end

def status_card(%{assigns: %{user: user}} = conn, %{"id" => status_id}) do
with %Activity{} = activity <- Activity.get_by_id(status_id),
true <- Visibility.visible_for_user?(activity, user) do


+ 9
- 2
lib/pleroma/web/mastodon_api/views/account_view.ex View File

@@ -72,6 +72,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
image = User.avatar_url(user) |> MediaProxy.url()
header = User.banner_url(user) |> MediaProxy.url()
user_info = User.get_cached_user_info(user)

following_count =
((!user.info.hide_follows or opts[:for] == user) && user_info.following_count) || 0

followers_count =
((!user.info.hide_followers or opts[:for] == user) && user_info.follower_count) || 0

bot = (user.info.source_data["type"] || "Person") in ["Application", "Service"]

emojis =
@@ -102,8 +109,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
display_name: display_name,
locked: user_info.locked,
created_at: Utils.to_masto_date(user.inserted_at),
followers_count: user_info.follower_count,
following_count: user_info.following_count,
followers_count: followers_count,
following_count: following_count,
statuses_count: user_info.note_count,
note: bio || "",
url: User.profile_url(user),


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

@@ -5,6 +5,8 @@
defmodule Pleroma.Web.MastodonAPI.StatusView do
use Pleroma.Web, :view

require Pleroma.Constants

alias Pleroma.Activity
alias Pleroma.HTML
alias Pleroma.Object
@@ -24,19 +26,19 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
defp get_replied_to_activities(activities) do
activities
|> Enum.map(fn
%{data: %{"type" => "Create", "object" => object}} ->
object = Object.normalize(object)
object.data["inReplyTo"] != "" && object.data["inReplyTo"]
%{data: %{"type" => "Create"}} = activity ->
object = Object.normalize(activity)
object && object.data["inReplyTo"] != "" && object.data["inReplyTo"]

_ ->
nil
end)
|> Enum.filter(& &1)
|> Activity.create_by_object_ap_id()
|> Activity.create_by_object_ap_id_with_object()
|> Repo.all()
|> Enum.reduce(%{}, fn activity, acc ->
object = Object.normalize(activity)
Map.put(acc, object.data["id"], activity)
if object, do: Map.put(acc, object.data["id"], activity), else: acc
end)
end

@@ -88,6 +90,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
reblogged_activity =
Activity.create_by_object_ap_id(activity_object.data["id"])
|> Activity.with_preloaded_bookmark(opts[:for])
|> Activity.with_set_thread_muted_field(opts[:for])
|> Repo.one()

reblogged = render("status.json", Map.put(opts, :activity, reblogged_activity))
@@ -142,6 +145,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
object = Object.normalize(activity)

user = get_user(activity.data["actor"])
user_follower_address = user.follower_address

like_count = object.data["like_count"] || 0
announcement_count = object.data["announcement_count"] || 0
@@ -157,7 +161,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
mentions =
(object.data["to"] ++ tag_mentions)
|> Enum.uniq()
|> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end)
|> Enum.map(fn
Pleroma.Constants.as_public() -> nil
^user_follower_address -> nil
ap_id -> User.get_cached_by_ap_id(ap_id)
end)
|> Enum.filter(& &1)
|> Enum.map(fn user -> AccountView.render("mention.json", %{user: user}) end)

@@ -168,7 +176,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
thread_muted? =
case activity.thread_muted? do
thread_muted? when is_boolean(thread_muted?) -> thread_muted?
nil -> CommonAPI.thread_muted?(user, activity)
nil -> (opts[:for] && CommonAPI.thread_muted?(opts[:for], activity)) || false
end

attachment_data = object.data["attachment"] || []


+ 4
- 50
lib/pleroma/web/nodeinfo/nodeinfo_controller.ex View File

@@ -34,64 +34,18 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
def raw_nodeinfo do
stats = Stats.get_stats()

exclusions = Config.get([:instance, :mrf_transparency_exclusions])

mrf_simple =
Config.get(:mrf_simple)
|> Enum.map(fn {k, v} -> {k, Enum.reject(v, fn v -> v in exclusions end)} end)
|> Enum.into(%{})

# This horror is needed to convert regex sigils to strings
mrf_keyword =
Config.get(:mrf_keyword, [])
|> Enum.map(fn {key, value} ->
{key,
Enum.map(value, fn
{pattern, replacement} ->
%{
"pattern" =>
if not is_binary(pattern) do
inspect(pattern)
else
pattern
end,
"replacement" => replacement
}

pattern ->
if not is_binary(pattern) do
inspect(pattern)
else
pattern
end
end)}
end)
|> Enum.into(%{})

mrf_policies =
MRF.get_policies()
|> Enum.map(fn policy -> to_string(policy) |> String.split(".") |> List.last() end)

quarantined = Config.get([:instance, :quarantined_instances], [])

staff_accounts =
User.all_superusers()
|> Enum.map(fn u -> u.ap_id end)

mrf_user_allowlist =
Config.get([:mrf_user_allowlist], [])
|> Enum.into(%{}, fn {k, v} -> {k, length(v)} end)

federation_response =
if Config.get([:instance, :mrf_transparency]) do
%{
mrf_policies: mrf_policies,
mrf_simple: mrf_simple,
mrf_keyword: mrf_keyword,
mrf_user_allowlist: mrf_user_allowlist,
quarantined_instances: quarantined,
exclusions: length(exclusions) > 0
}
{:ok, data} = MRF.describe()

data
|> Map.merge(%{quarantined_instances: quarantined})
else
%{}
end


+ 10
- 16
lib/pleroma/web/oauth/token/clean_worker.ex View File

@@ -6,36 +6,30 @@ defmodule Pleroma.Web.OAuth.Token.CleanWorker do
@moduledoc """
The module represents functions to clean an expired oauth tokens.
"""
use GenServer

@ten_seconds 10_000
@one_day 86_400_000

# 10 seconds
@start_interval 10_000
@interval Pleroma.Config.get(
# 24 hours
[:oauth2, :clean_expired_tokens_interval],
86_400_000
@one_day
)
@queue :background

alias Pleroma.Web.OAuth.Token

def start_link, do: GenServer.start_link(__MODULE__, nil)
def start_link(_), do: GenServer.start_link(__MODULE__, %{})

def init(_) do
if Pleroma.Config.get([:oauth2, :clean_expired_tokens], false) do
Process.send_after(self(), :perform, @start_interval)
{:ok, nil}
else
:ignore
end
Process.send_after(self(), :perform, @ten_seconds)
{:ok, nil}
end

@doc false
def handle_info(:perform, state) do
Token.delete_expired_tokens()

Process.send_after(self(), :perform, @interval)
PleromaJobQueue.enqueue(@queue, __MODULE__, [:clean])
{:noreply, state}
end

# Job Worker Callbacks
def perform(:clean), do: Token.delete_expired_tokens()
end

+ 6
- 13
lib/pleroma/web/streamer.ex View File

@@ -18,7 +18,7 @@ defmodule Pleroma.Web.Streamer do

@keepalive_interval :timer.seconds(30)

def start_link do
def start_link(_) do
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end

@@ -35,28 +35,21 @@ defmodule Pleroma.Web.Streamer do
end

def init(args) do
spawn(fn ->
# 30 seconds
Process.sleep(@keepalive_interval)
GenServer.cast(__MODULE__, %{action: :ping})
end)
Process.send_after(self(), %{action: :ping}, @keepalive_interval)

{:ok, args}
end

def handle_cast(%{action: :ping}, topics) do
Map.values(topics)
def handle_info(%{action: :ping}, topics) do
topics
|> Map.values()
|> List.flatten()
|> Enum.each(fn socket ->
Logger.debug("Sending keepalive ping")
send(socket.transport_pid, {:text, ""})
end)

spawn(fn ->
# 30 seconds
Process.sleep(@keepalive_interval)
GenServer.cast(__MODULE__, %{action: :ping})
end)
Process.send_after(self(), %{action: :ping}, @keepalive_interval)

{:noreply, topics}
end


+ 2
- 2
lib/pleroma/web/web.ex View File

@@ -58,10 +58,10 @@ defmodule Pleroma.Web do
rescue
error ->
Logger.error(
"#{__MODULE__} failed to render #{inspect({view, template})}: #{inspect(error)}"
"#{__MODULE__} failed to render #{inspect({view, template})}\n" <>
Exception.format(:error, error, __STACKTRACE__)
)

Logger.error(inspect(__STACKTRACE__))
nil
end



+ 3
- 2
mix.exs View File

@@ -95,7 +95,7 @@ defmodule Pleroma.Mixfile do
defp deps do
[
{:phoenix, "~> 1.4.8"},
{:tzdata, "~> 1.0"},
{:tzdata, "~> 0.5.21"},
{:plug_cowboy, "~> 2.0"},
{:phoenix_pubsub, "~> 1.1"},
{:phoenix_ecto, "~> 4.0"},
@@ -114,8 +114,9 @@ defmodule Pleroma.Mixfile do
{:tesla, "~> 1.2"},
{:jason, "~> 1.0"},
{:mogrify, "~> 0.6.1"},
{:ex_aws, "~> 2.0"},
{:ex_aws, "~> 2.1"},
{:ex_aws_s3, "~> 2.0"},
{:sweet_xml, "~> 0.6.6"},
{:earmark, "~> 1.3"},
{:bbcode, "~> 0.1.1"},
{:ex_machina, "~> 2.3", only: :test},


+ 2
- 1
mix.lock View File

@@ -80,13 +80,14 @@
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
"recon": {:git, "https://github.com/ferd/recon.git", "75d70c7c08926d2f24f1ee6de14ee50fe8a52763", [tag: "2.4.0"]},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm"},
"swoosh": {:hex, :swoosh, "0.23.2", "7dda95ff0bf54a2298328d6899c74dae1223777b43563ccebebb4b5d2b61df38", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13", [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]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
"syslog": {:git, "https://github.com/Vagabond/erlang-syslog.git", "4a6c6f2c996483e86c1320e9553f91d337bcb6aa", [tag: "1.0.5"]},
"telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"},
"tesla": {:hex, :tesla, "1.2.1", "864783cc27f71dd8c8969163704752476cec0f3a51eb3b06393b3971dc9733ff", [:mix], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},
"timex": {:hex, :timex, "3.6.1", "efdf56d0e67a6b956cc57774353b0329c8ab7726766a11547e529357ffdc1d56", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"tzdata": {:hex, :tzdata, "1.0.1", "f6027a331af7d837471248e62733c6ebee86a72e57c613aa071ebb1f750fc71a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"tzdata": {:hex, :tzdata, "0.5.21", "8cbf3607fcce69636c672d5be2bbb08687fe26639a62bdcc283d267277db7cf0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"ueberauth": {:hex, :ueberauth, "0.6.1", "9e90d3337dddf38b1ca2753aca9b1e53d8a52b890191cdc55240247c89230412", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
"unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm"},


+ 1
- 0
priv/static/adminfe/chunk-0e18.e12401fb.css View File

@@ -0,0 +1 @@
header[data-v-71c7ded0]{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;margin:22px 0;padding-left:15px}header h1[data-v-71c7ded0]{margin:0 0 0 10px}table[data-v-71c7ded0]{margin:10px 0 0 15px}table .name-col[data-v-71c7ded0]{width:150px}.el-table--border[data-v-71c7ded0]:after,.el-table--group[data-v-71c7ded0]:after,.el-table[data-v-71c7ded0]:before{background-color:transparent}.poll ul[data-v-71c7ded0]{list-style-type:none;padding:0;width:30%}.image[data-v-71c7ded0]{width:20%}.image img[data-v-71c7ded0]{width:100%}.statuses[data-v-71c7ded0]{padding-right:20px}.show-private[data-v-71c7ded0]{text-align:right;line-height:67px;padding-right:20px}

priv/static/adminfe/chunk-56c9.c27dac5e.css → priv/static/adminfe/chunk-1fbf.d7a1893c.css View File

@@ -1 +1 @@
.status-account{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.status-avatar-img{width:15px;height:15px;margin-right:5px}.status-account-name{margin:0;height:22px}.status-body{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.status-content{font-size:15px}.status-card{margin-bottom:15px}.status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.el-message{min-width:80%}.el-message-box{width:80%}.status-card .el-card__header{padding:10px 17px}.status-card .el-tag{margin:3px 4px 3px 0}.status-card .status-account-container{margin-bottom:5px}.status-card .status-actions-button{margin:3px 0}.status-card .status-actions{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.status-card .status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}.account{text-decoration:underline}.avatar-img{vertical-align:bottom;width:15px;height:15px;margin-left:5px}.el-card__body{padding:17px}.el-card__header{background-color:#fafafa;padding:10px 20px}.el-collapse{border-bottom:none}.el-collapse-item__header{height:46px;font-size:14px}.el-collapse-item__content{padding-bottom:7px}.el-icon-arrow-right{margin-right:6px}.el-icon-close{padding:10px 5px 10px 10px;cursor:pointer}h4{margin:0;height:17px}.header-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:baseline;-ms-flex-align:baseline;align-items:baseline;height:40px}.id{color:grey;margin-top:6px}.line{width:100%;height:0;border:.5px solid #ebeef5;margin:15px 0}.new-note p{font-size:14px;font-weight:500;height:17px;margin:13px 0 7px}.note{-webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,.1);box-shadow:0 2px 5px 0 rgba(0,0,0,.1);margin-bottom:10px}.no-notes{font-style:italic;color:grey}.report-row-key{font-weight:500;font-size:14px}.report-title{margin:0}.statuses{margin-top:15px}.submit-button{display:block;margin:7px 0 17px auto}.timestamp{margin:0;font-style:italic;color:grey}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.timeline-item-container .header-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:80px}.timeline-item-container .id{margin:6px 0 0}}.select-field[data-v-bb4390da]{width:350px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.select-field[data-v-bb4390da]{width:100%;margin-bottom:5px}}.reports-container .el-timeline[data-v-e32c7dc6]{margin:45px 45px 45px 19px;padding:0}.reports-container .filter-container[data-v-e32c7dc6]{margin:22px 15px;padding-bottom:0}.reports-container h1[data-v-e32c7dc6]{margin:22px 0 0 15px}.reports-container .no-reports-message[data-v-e32c7dc6]{color:grey;margin-left:19px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.reports-container h1[data-v-e32c7dc6]{margin:7px 10px 15px}.reports-container .filter-container[data-v-e32c7dc6]{margin:0 10px}.reports-container .timeline[data-v-e32c7dc6]{margin:20px 20px 20px 18px}}
.status-account{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.status-avatar-img{width:15px;height:15px;margin-right:5px}.status-account-name{margin:0;height:22px}.status-body{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.status-content{font-size:15px}.status-card{margin-bottom:15px}.status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.el-message{min-width:80%}.el-message-box{width:80%}.status-card .el-card__header{padding:10px 17px}.status-card .el-tag{margin:3px 4px 3px 0}.status-card .status-account-container{margin-bottom:5px}.status-card .status-actions-button{margin:3px 0}.status-card .status-actions{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.status-card .status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}.account{text-decoration:underline}.avatar-img{vertical-align:bottom;width:15px;height:15px;margin-left:5px}.el-card__body{padding:17px}.el-card__header{background-color:#fafafa;padding:10px 20px}.el-collapse{border-bottom:none}.el-collapse-item__header{height:46px;font-size:14px}.el-collapse-item__content{padding-bottom:7px}.el-icon-arrow-right{margin-right:6px}.el-icon-close{padding:10px 5px 10px 10px;cursor:pointer}h4{margin:0;height:17px}.header-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:baseline;-ms-flex-align:baseline;align-items:baseline;height:40px}.id{color:grey;margin-top:6px}.line{width:100%;height:0;border:.5px solid #ebeef5;margin:15px 0}.new-note p{font-size:14px;font-weight:500;height:17px;margin:13px 0 7px}.note{-webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,.1);box-shadow:0 2px 5px 0 rgba(0,0,0,.1);margin-bottom:10px}.no-notes{font-style:italic;color:grey}.report-row-key{font-weight:500;font-size:14px}.report-title{margin:0}.statuses{margin-top:15px}.submit-button{display:block;margin:7px 0 17px auto}.timestamp{margin:0;font-style:italic;color:grey}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.timeline-item-container .header-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:80px}.timeline-item-container .id{margin:6px 0 0}}.select-field[data-v-07695bc4]{width:350px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.select-field[data-v-07695bc4]{width:100%;margin-bottom:5px}}.reports-container .el-timeline[data-v-e32c7dc6]{margin:45px 45px 45px 19px;padding:0}.reports-container .filter-container[data-v-e32c7dc6]{margin:22px 15px;padding-bottom:0}.reports-container h1[data-v-e32c7dc6]{margin:22px 0 0 15px}.reports-container .no-reports-message[data-v-e32c7dc6]{color:grey;margin-left:19px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.reports-container h1[data-v-e32c7dc6]{margin:7px 10px 15px}.reports-container .filter-container[data-v-e32c7dc6]{margin:0 10px}.reports-container .timeline[data-v-e32c7dc6]{margin:20px 20px 20px 18px}}

priv/static/adminfe/chunk-f018.0d22684d.css → priv/static/adminfe/chunk-2325.0d22684d.css View File


+ 1
- 0
priv/static/adminfe/chunk-5e57.ac97b15a.css View File

@@ -0,0 +1 @@
a{text-decoration:underline}.code{background-color:rgba(173,190,214,.48);border-radius:3px;font-family:monospace;padding:0 3px}.el-form-item{margin-right:30px}.el-select{width:100%}.esshd-list{margin:0}.expl{color:#666;font-size:13px;line-height:22px;margin:5px 0 0;overflow-wrap:break-word}.highlight{background-color:#e6e6e6}.limit-button-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:baseline;-ms-flex-align:baseline;align-items:baseline}.limit-expl{margin-left:10px}.limit-input{width:48%;margin:0 0 5px 8px}.line{width:100%;height:0;border:1px solid #eee;margin-bottom:22px}.mascot-container{margin-bottom:15px}.mascot-input{margin-bottom:7px}.mascot-name-container{display:-webkit-box;display:-ms-flexbox;display:flex;margin-bottom:7px}.mascot-name-input{margin-right:10px}.name-input{width:30%;margin-right:8px}.options-paragraph{font-size:14px;color:#606266;font-family:Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei;font-weight:700;line-height:20px;margin:0 0 14px}.options-paragraph-container{overflow-wrap:break-word;margin-bottom:0}.pattern-input{width:20%;margin-right:8px}.setting-input{display:-webkit-box;display:-ms-flexbox;display:flex;margin-bottom:10px}.single-input{margin-right:10px}.scale-input{width:48%;margin:0 8px 5px 0}.replacement-input{width:80%;margin-left:8px;margin-right:10px}.text{line-height:20px;margin-right:15px}.upload-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:baseline;-ms-flex-align:baseline;align-items:baseline}.value-input{width:70%;margin-left:8px;margin-right:10px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.el-form-item{margin-right:15px}.el-input__inner{padding:0 5px}.el-form-item__label:not(.no-top-margin){padding-left:3px;padding-right:10px;line-height:22px;margin-top:7px}.el-message{min-width:80%}.el-select__tags{overflow:hidden}.name-input{width:40%;margin-right:5px}p.expl{line-height:20px}.pattern-input{width:40%;margin-right:4px}.replacement-input{width:60%;margin-left:4px;margin-right:5px}.top-margin{position:absolute;top:25%}.value-input{width:60%;margin-left:5px;margin-right:8px}}.settings-container .el-tabs[data-v-729534ce]{margin-top:20px}.settings-container h1[data-v-729534ce]{margin:22px 0 0 15px}

priv/static/adminfe/chunk-5eaf.1a04e979.css → priv/static/adminfe/chunk-e547.e4b6230b.css View File

@@ -1 +1 @@
.select-field[data-v-71bc6b38]{width:350px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.select-field[data-v-71bc6b38]{width:100%;margin-bottom:5px}}.actions-button[data-v-94227b1e]{text-align:left;width:350px;padding:10px}.actions-button-container[data-v-94227b1e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.el-dropdown[data-v-94227b1e]{float:right}.el-icon-edit[data-v-94227b1e]{margin-right:5px}.tag-container[data-v-94227b1e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.tag-text[data-v-94227b1e]{padding-right:20px}.no-hover[data-v-94227b1e]:hover{color:#606266;background-color:#fff;cursor:auto}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.create-user-dialog{width:80%}.create-account-form-item{margin-bottom:30px}.el-dialog__body{padding:20px 20px 0}}.actions-button[data-v-3ffddd00]{text-align:left;width:350px;padding:10px}.actions-container[data-v-3ffddd00]{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:0 15px 10px}.active-tag[data-v-3ffddd00]{color:#409eff;font-weight:700}.active-tag .el-icon-check[data-v-3ffddd00]{color:#409eff;float:right;margin:7px 0 0 15px}.el-dropdown-link[data-v-3ffddd00]:hover{cursor:pointer;color:#409eff}.el-icon-plus[data-v-3ffddd00]{margin-right:5px}.users-container h1[data-v-3ffddd00]{margin:22px 0 0 15px}.users-container .pagination[data-v-3ffddd00]{margin:25px 0;text-align:center}.users-container .search[data-v-3ffddd00]{width:350px;float:right}.users-container .filter-container[data-v-3ffddd00]{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:22px 15px 15px}.users-container .user-count[data-v-3ffddd00]{color:grey;font-size:28px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.users-container h1[data-v-3ffddd00]{margin:7px 10px 15px}.users-container .actions-container[data-v-3ffddd00]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:0 10px 7px}.users-container .create-account[data-v-3ffddd00]{width:100%}.users-container .el-icon-arrow-down[data-v-3ffddd00]{font-size:12px}.users-container .search[data-v-3ffddd00]{width:100%}.users-container .filter-container[data-v-3ffddd00]{display:-webkit-box;display:-ms-flexbox;display:flex;height:82px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:0 10px}.users-container .el-tag[data-v-3ffddd00]{width:30px;display:inline-block;margin-bottom:4px;font-weight:700}.users-container .el-tag.el-tag--danger[data-v-3ffddd00],.users-container .el-tag.el-tag--success[data-v-3ffddd00]{padding-left:8px}}
.select-field[data-v-71bc6b38]{width:350px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.select-field[data-v-71bc6b38]{width:100%;margin-bottom:5px}}.actions-button[data-v-94227b1e]{text-align:left;width:350px;padding:10px}.actions-button-container[data-v-94227b1e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.el-dropdown[data-v-94227b1e]{float:right}.el-icon-edit[data-v-94227b1e]{margin-right:5px}.tag-container[data-v-94227b1e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.tag-text[data-v-94227b1e]{padding-right:20px}.no-hover[data-v-94227b1e]:hover{color:#606266;background-color:#fff;cursor:auto}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.create-user-dialog{width:80%}.create-account-form-item{margin-bottom:30px}.el-dialog__body{padding:20px 20px 0}}.actions-button[data-v-c51cd8ee]{text-align:left;width:350px;padding:10px}.actions-container[data-v-c51cd8ee]{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:0 15px 10px}.active-tag[data-v-c51cd8ee]{color:#409eff;font-weight:700}.active-tag .el-icon-check[data-v-c51cd8ee]{color:#409eff;float:right;margin:7px 0 0 15px}.el-dropdown-link[data-v-c51cd8ee]:hover{cursor:pointer;color:#409eff}.el-icon-plus[data-v-c51cd8ee]{margin-right:5px}.users-container h1[data-v-c51cd8ee]{margin:22px 0 0 15px}.users-container .pagination[data-v-c51cd8ee]{margin:25px 0;text-align:center}.users-container .search[data-v-c51cd8ee]{width:350px;float:right}.users-container .filter-container[data-v-c51cd8ee]{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:22px 15px 15px}.users-container .user-count[data-v-c51cd8ee]{color:grey;font-size:28px}@media (min-device-width:768px) and (max-device-width:1024px),only screen and (max-width:760px){.users-container h1[data-v-c51cd8ee]{margin:7px 10px 15px}.users-container .actions-container[data-v-c51cd8ee]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:0 10px 7px}.users-container .create-account[data-v-c51cd8ee]{width:100%}.users-container .el-icon-arrow-down[data-v-c51cd8ee]{font-size:12px}.users-container .search[data-v-c51cd8ee]{width:100%}.users-container .filter-container[data-v-c51cd8ee]{display:-webkit-box;display:-ms-flexbox;display:flex;height:82px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:0 10px}.users-container .el-tag[data-v-c51cd8ee]{width:30px;display:inline-block;margin-bottom:4px;font-weight:700}.users-container .el-tag.el-tag--danger[data-v-c51cd8ee],.users-container .el-tag.el-tag--success[data-v-c51cd8ee]{padding-left:8px}}

+ 1
- 0
priv/static/adminfe/chunk-elementUI.e5cd8da6.css
File diff suppressed because it is too large
View File


+ 0
- 1
priv/static/adminfe/chunk-elementUI.f74c256b.css
File diff suppressed because it is too large
View File


+ 1
- 1
priv/static/adminfe/index.html View File

@@ -1 +1 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=renderer content=webkit><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Admin FE</title><link rel="shortcut icon" href=favicon.ico><link href=chunk-elementUI.f74c256b.css rel=stylesheet><link href=chunk-libs.4e8c4664.css rel=stylesheet><link href=app.34fc670f.css rel=stylesheet></head><body><script src=/pleroma/admin/static/tinymce4.7.5/tinymce.min.js></script><div id=app></div><script type=text/javascript src=static/js/runtime.d8d12c12.js></script><script type=text/javascript src=static/js/chunk-elementUI.1fa5434b.js></script><script type=text/javascript src=static/js/chunk-libs.d5609760.js></script><script type=text/javascript src=static/js/app.4137ad8f.js></script></body></html>
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=renderer content=webkit><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Admin FE</title><link rel="shortcut icon" href=favicon.ico><link href=chunk-elementUI.e5cd8da6.css rel=stylesheet><link href=chunk-libs.4e8c4664.css rel=stylesheet><link href=app.34fc670f.css rel=stylesheet></head><body><script src=/pleroma/admin/static/tinymce4.7.5/tinymce.min.js></script><div id=app></div><script type=text/javascript src=static/js/runtime.f40c8ec4.js></script><script type=text/javascript src=static/js/chunk-elementUI.1911151b.js></script><script type=text/javascript src=static/js/chunk-libs.fb0b7f4a.js></script><script type=text/javascript src=static/js/app.8e186193.js></script></body></html>

BIN
priv/static/adminfe/static/fonts/element-icons.2fad952.woff View File


BIN
priv/static/adminfe/static/fonts/element-icons.535877f.woff View File


BIN
priv/static/adminfe/static/fonts/element-icons.6f0a763.ttf View File


BIN
priv/static/adminfe/static/fonts/element-icons.732389d.ttf View File


+ 0
- 1
priv/static/adminfe/static/js/app.4137ad8f.js
File diff suppressed because it is too large
View File


+ 1
- 0
priv/static/adminfe/static/js/app.8e186193.js
File diff suppressed because it is too large
View File


+ 1
- 0
priv/static/adminfe/static/js/chunk-0e18.208cd826.js View File

@@ -0,0 +1 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-0e18"],{"4bFr":function(t,e,s){"use strict";s.r(e);var a={name:"UsersShow",data:function(){return{showPrivate:!1}},computed:{loading:function(){return this.$store.state.userProfile.loading},user:function(){return this.$store.state.userProfile.user},statuses:function(){return this.$store.state.userProfile.statuses}},mounted:function(){this.$store.dispatch("FetchData",{id:this.$route.params.id,godmode:!1})},methods:{optionPercent:function(t,e){var s=t.options.reduce(function(t,e){return t+e.votes_count},0);return 0===s?0:+(e.votes_count/s*100).toFixed(1)},createdAtLocaleString:function(t){var e=new Date(t);return"".concat(e.toLocaleDateString()," ").concat(e.toLocaleTimeString())},onTogglePrivate:function(){console.log(this.showPrivate),this.$store.dispatch("FetchData",{id:this.$route.params.id,godmode:this.showPrivate})}}},r=(s("QG2t"),s("KHd+")),l=Object(r.a)(a,function(){var t=this,e=t.$createElement,s=t._self._c||e;return t.loading?t._e():s("main",[s("header",[s("el-avatar",{attrs:{src:t.user.avatar,size:"large"}}),t._v(" "),s("h1",[t._v(t._s(t.user.display_name))])],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:6}},[s("div",{staticClass:"el-table el-table--fit el-table--enable-row-hover el-table--enable-row-transition el-table--medium"},[s("table",{staticClass:"el-table__body"},[s("tbody",[s("tr",{staticClass:"el-table__row"},[s("td",{staticClass:"name-col"},[t._v("ID")]),t._v(" "),s("td",{staticClass:"value-col"},[t._v("\n "+t._s(t.user.id)+"\n ")])]),t._v(" "),s("tr",{staticClass:"el-table__row"},[s("td",[t._v(t._s(t.$t("userProfile.tags")))]),t._v(" "),s("td",[t._l(t.user.tags,function(e){return s("el-tag",{key:e},[t._v(t._s(e))])}),t._v(" "),0===t.user.tags.length?s("span",[t._v("None")]):t._e()],2)]),t._v(" "),s("tr",{staticClass:"el-table__row"},[s("td",[t._v(t._s(t.$t("userProfile.moderator")))]),t._v(" "),s("td",[t.user.roles.moderator?s("el-tag",{attrs:{type:"success"}},[s("i",{staticClass:"el-icon-check"})]):t._e(),t._v(" "),t.user.roles.moderator?t._e():s("el-tag",{attrs:{type:"danger"}},[s("i",{staticClass:"el-icon-error"})])],1)]),t._v(" "),s("tr",{staticClass:"el-table__row"},[s("td",[t._v(t._s(t.$t("userProfile.admin")))]),t._v(" "),s("td",[t.user.roles.admin?s("el-tag",{attrs:{type:"success"}},[s("i",{staticClass:"el-icon-check"})]):t._e(),t._v(" "),t.user.roles.admin?t._e():s("el-tag",{attrs:{type:"danger"}},[s("i",{staticClass:"el-icon-error"})])],1)]),t._v(" "),s("tr",{staticClass:"el-table__row"},[s("td",[t._v(t._s(t.$t("userProfile.local")))]),t._v(" "),s("td",[t.user.local?s("el-tag",{attrs:{type:"success"}},[s("i",{staticClass:"el-icon-check"})]):t._e(),t._v(" "),t.user.local?t._e():s("el-tag",{attrs:{type:"danger"}},[s("i",{staticClass:"el-icon-error"})])],1)]),t._v(" "),s("tr",{staticClass:"el-table__row"},[s("td",[t._v(t._s(t.$t("userProfile.deactivated")))]),t._v(" "),s("td",[t.user.deactivated?s("el-tag",{attrs:{type:"success"}},[s("i",{staticClass:"el-icon-check"})]):t._e(),t._v(" "),t.user.deactivated?t._e():s("el-tag",{attrs:{type:"danger"}},[s("i",{staticClass:"el-icon-error"})])],1)]),t._v(" "),s("tr",{staticClass:"el-table__row"},[s("td",[t._v(t._s(t.$t("userProfile.nickname")))]),t._v(" "),s("td",[t._v("\n "+t._s(t.user.nickname)+"\n ")])])])])])]),t._v(" "),s("el-row",{staticClass:"row-bg",attrs:{type:"flex",justify:"space-between"}},[s("el-col",{attrs:{span:18}},[s("h2",[t._v(t._s(t.$t("userProfile.recentStatuses")))])]),t._v(" "),s("el-col",{staticClass:"show-private",attrs:{span:6}},[s("el-checkbox",{on:{change:t.onTogglePrivate},model:{value:t.showPrivate,callback:function(e){t.showPrivate=e},expression:"showPrivate"}},[t._v("\n "+t._s(t.$t("userProfile.showPrivateStatuses"))+"\n ")])],1)],1),t._v(" "),s("el-col",{attrs:{span:18}},[s("el-timeline",{staticClass:"statuses"},t._l(t.statuses,function(e){return s("el-timeline-item",{key:e.id,attrs:{timestamp:t.createdAtLocaleString(e.created_at)}},[s("el-card",[e.spoiler_text?s("strong",[t._v(t._s(e.spoiler_text))]):t._e(),t._v(" "),e.content?s("p",{domProps:{innerHTML:t._s(e.content)}}):t._e(),t._v(" "),e.poll?s("div",{staticClass:"poll"},[s("ul",t._l(e.poll.options,function(a,r){return s("li",{key:r},[t._v("\n "+t._s(a.title)+"\n "),s("el-progress",{attrs:{percentage:t.optionPercent(e.poll,a)}})],1)}),0)]):t._e(),t._v(" "),t._l(e.media_attachments,function(t,e){return s("div",{key:e,staticClass:"image"},[s("img",{attrs:{src:t.preview_url}})])})],2)],1)}),1)],1)],1)],1)},[],!1,null,"71c7ded0",null);l.options.__file="show.vue";e.default=l.exports},QG2t:function(t,e,s){"use strict";var a=s("R7Mx");s.n(a).a},R7Mx:function(t,e,s){}}]);

+ 1
- 0
priv/static/adminfe/static/js/chunk-1fbf.616fb309.js
File diff suppressed because it is too large
View File


priv/static/adminfe/static/js/chunk-2325.154a537b.js
File diff suppressed because it is too large
View File


+ 0
- 1
priv/static/adminfe/static/js/chunk-56c9.28e35fc3.js
File diff suppressed because it is too large
View File


+ 1
- 0
priv/static/adminfe/static/js/chunk-5e57.7313703a.js
File diff suppressed because it is too large
View File


+ 0
- 1
priv/static/adminfe/static/js/chunk-5eaf.5b76e416.js
File diff suppressed because it is too large
View File


+ 1
- 0
priv/static/adminfe/static/js/chunk-7fe2.458f9da5.js
File diff suppressed because it is too large
View File


+ 1
- 0
priv/static/adminfe/static/js/chunk-e547.d57d1b91.js
File diff suppressed because it is too large
View File


+ 1
- 0
priv/static/adminfe/static/js/chunk-elementUI.1911151b.js
File diff suppressed because it is too large
View File


+ 0
- 1
priv/static/adminfe/static/js/chunk-elementUI.1fa5434b.js
File diff suppressed because it is too large
View File


priv/static/adminfe/static/js/chunk-libs.fb0b7f4a.js
File diff suppressed because it is too large
View File


+ 0
- 1
priv/static/adminfe/static/js/runtime.d8d12c12.js View File

@@ -1 +0,0 @@
!function(e){function t(t){for(var r,o,a=t[0],i=t[1],f=t[2],l=0,h=[];l<a.length;l++)o=a[l],u[o]&&h.push(u[o][0]),u[o]=0;for(r in i)Object.prototype.hasOwnProperty.call(i,r)&&(e[r]=i[r]);for(s&&s(t);h.length;)h.shift()();return c.push.apply(c,f||[]),n()}function n(){for(var e,t=0;t<c.length;t++){for(var n=c[t],r=!0,o=1;o<n.length;o++){var i=n[o];0!==u[i]&&(r=!1)}r&&(c.splice(t--,1),e=a(a.s=n[0]))}return e}var r={},o={runtime:0},u={runtime:0},c=[];function a(t){if(r[t])return r[t].exports;var n=r[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,a),n.l=!0,n.exports}a.e=function(e){var t=[];o[e]?t.push(o[e]):0!==o[e]&&{"chunk-56c9":1,"chunk-5eaf":1,"chunk-18e1":1,"chunk-8b70":1,"chunk-f018":1}[e]&&t.push(o[e]=new Promise(function(t,n){for(var r=({}[e]||e)+"."+{"7zzA":"31d6cfe0",JEtC:"31d6cfe0","chunk-02a0":"31d6cfe0","chunk-56c9":"c27dac5e","chunk-0620":"31d6cfe0","chunk-5eaf":"1a04e979","chunk-18e1":"6aaab273","chunk-8b70":"9ba0945c","chunk-f018":"0d22684d"}[e]+".css",o=a.p+r,u=document.getElementsByTagName("link"),c=0;c<u.length;c++){var i=(l=u[c]).getAttribute("data-href")||l.getAttribute("href");if("stylesheet"===l.rel&&(i===r||i===o))return t()}var f=document.getElementsByTagName("style");for(c=0;c<f.length;c++){var l;if((i=(l=f[c]).getAttribute("data-href"))===r||i===o)return t()}var s=document.createElement("link");s.rel="stylesheet",s.type="text/css",s.onload=t,s.onerror=function(t){var r=t&&t.target&&t.target.src||o,u=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");u.request=r,n(u)},s.href=o,document.getElementsByTagName("head")[0].appendChild(s)}).then(function(){o[e]=0}));var n=u[e];if(0!==n)if(n)t.push(n[2]);else{var r=new Promise(function(t,r){n=u[e]=[t,r]});t.push(n[2]=r);var c,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+"static/js/"+({}[e]||e)+"."+{"7zzA":"e1ae1c94",JEtC:"f9ba4594","chunk-02a0":"db6ec114","chunk-56c9":"28e35fc3","chunk-0620":"c765c190","chunk-5eaf":"5b76e416","chunk-18e1":"7f9c377c","chunk-8b70":"46525646","chunk-f018":"e1a7a454"}[e]+".js"}(e),c=function(t){i.onerror=i.onload=null,clearTimeout(f);var n=u[e];if(0!==n){if(n){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src,c=new Error("Loading chunk "+e+" failed.\n("+r+": "+o+")");c.type=r,c.request=o,n[1](c)}u[e]=void 0}};var f=setTimeout(function(){c({type:"timeout",target:i})},12e4);i.onerror=i.onload=c,document.head.appendChild(i)}return Promise.all(t)},a.m=e,a.c=r,a.d=function(e,t,n){a.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,t){if(1&t&&(e=a(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(a.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)a.d(n,r,function(t){return e[t]}.bind(null,r));return n},a.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(t,"a",t),t},a.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},a.p="",a.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],f=i.push.bind(i);i.push=t,i=i.slice();for(var l=0;l<i.length;l++)t(i[l]);var s=f;n()}([]);

+ 1
- 0
priv/static/adminfe/static/js/runtime.f40c8ec4.js View File

@@ -0,0 +1 @@
!function(e){function n(n){for(var r,u,a=n[0],f=n[1],i=n[2],l=0,s=[];l<a.length;l++)u=a[l],c[u]&&s.push(c[u][0]),c[u]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);for(h&&h(n);s.length;)s.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,u=1;u<t.length;u++){var f=t[u];0!==c[f]&&(r=!1)}r&&(o.splice(n--,1),e=a(a.s=t[0]))}return e}var r={},u={runtime:0},c={runtime:0},o=[];function a(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var n=[];u[e]?n.push(u[e]):0!==u[e]&&{"chunk-1fbf":1,"chunk-e547":1,"chunk-0e18":1,"chunk-18e1":1,"chunk-2325":1,"chunk-5e57":1,"chunk-8b70":1}[e]&&n.push(u[e]=new Promise(function(n,t){for(var r=({}[e]||e)+"."+{"7zzA":"31d6cfe0",JEtC:"31d6cfe0","chunk-02a0":"31d6cfe0","chunk-1fbf":"d7a1893c","chunk-0620":"31d6cfe0","chunk-e547":"e4b6230b","chunk-0e18":"e12401fb","chunk-18e1":"6aaab273","chunk-2325":"0d22684d","chunk-7fe2":"31d6cfe0","chunk-5e57":"ac97b15a","chunk-8b70":"9ba0945c"}[e]+".css",u=a.p+r,c=document.getElementsByTagName("link"),o=0;o<c.length;o++){var f=(l=c[o]).getAttribute("data-href")||l.getAttribute("href");if("stylesheet"===l.rel&&(f===r||f===u))return n()}var i=document.getElementsByTagName("style");for(o=0;o<i.length;o++){var l;if((f=(l=i[o]).getAttribute("data-href"))===r||f===u)return n()}var h=document.createElement("link");h.rel="stylesheet",h.type="text/css",h.onload=n,h.onerror=function(n){var r=n&&n.target&&n.target.src||u,c=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");c.request=r,t(c)},h.href=u,document.getElementsByTagName("head")[0].appendChild(h)}).then(function(){u[e]=0}));var t=c[e];if(0!==t)if(t)n.push(t[2]);else{var r=new Promise(function(n,r){t=c[e]=[n,r]});n.push(t[2]=r);var o,f=document.createElement("script");f.charset="utf-8",f.timeout=120,a.nc&&f.setAttribute("nonce",a.nc),f.src=function(e){return a.p+"static/js/"+({}[e]||e)+"."+{"7zzA":"e1ae1c94",JEtC:"f9ba4594","chunk-02a0":"db6ec114","chunk-1fbf":"616fb309","chunk-0620":"c765c190","chunk-e547":"d57d1b91","chunk-0e18":"208cd826","chunk-18e1":"7f9c377c","chunk-2325":"154a537b","chunk-7fe2":"458f9da5","chunk-5e57":"7313703a","chunk-8b70":"46525646"}[e]+".js"}(e),o=function(n){f.onerror=f.onload=null,clearTimeout(i);var t=c[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),u=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+u+")");o.type=r,o.request=u,t[1](o)}c[e]=void 0}};var i=setTimeout(function(){o({type:"timeout",target:f})},12e4);f.onerror=f.onload=o,document.head.appendChild(f)}return Promise.all(n)},a.m=e,a.c=r,a.d=function(e,n,t){a.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,n){if(1&n&&(e=a(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)a.d(t,r,function(n){return e[n]}.bind(null,r));return t},a.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(n,"a",n),n},a.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},a.p="",a.oe=function(e){throw console.error(e),e};var f=window.webpackJsonp=window.webpackJsonp||[],i=f.push.bind(f);f.push=n,f=f.slice();for(var l=0;l<f.length;l++)n(f[l]);var h=i;t()}([]);

+ 2
- 2
test/config/transfer_task_test.exs View File

@@ -31,7 +31,7 @@ defmodule Pleroma.Config.TransferTaskTest do
value: [live: 15, com: 35]
})

Pleroma.Config.TransferTask.start_link()
Pleroma.Config.TransferTask.start_link([])

assert Application.get_env(:pleroma, :test_key) == [live: 2, com: 3]
assert Application.get_env(:idna, :test_key) == [live: 15, com: 35]
@@ -50,7 +50,7 @@ defmodule Pleroma.Config.TransferTaskTest do
})

assert ExUnit.CaptureLog.capture_log(fn ->
Pleroma.Config.TransferTask.start_link()
Pleroma.Config.TransferTask.start_link([])
end) =~
"updating env causes error, key: \"undefined_atom_key\", error: %ArgumentError{message: \"argument error\"}"
end


+ 13
- 0
test/support/mrf_module_mock.ex View File

@@ -0,0 +1,13 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule MRFModuleMock do
@behaviour Pleroma.Web.ActivityPub.MRF

@impl true
def filter(message), do: {:ok, message}

@impl true
def describe, do: {:ok, %{mrf_module_mock: "some config data"}}
end

+ 36
- 0
test/tasks/database_test.exs View File

@@ -3,8 +3,11 @@
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Mix.Tasks.Pleroma.DatabaseTest do
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.CommonAPI

use Pleroma.DataCase

import Pleroma.Factory
@@ -46,4 +49,37 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest do
assert user.info.follower_count == 0
end
end

describe "running fix_likes_collections" do
test "it turns OrderedCollection likes into empty arrays" do
[user, user2] = insert_pair(:user)

{:ok, %{id: id, object: object}} = CommonAPI.post(user, %{"status" => "test"})
{:ok, %{object: object2}} = CommonAPI.post(user, %{"status" => "test test"})

CommonAPI.favorite(id, user2)

likes = %{
"first" =>
"http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes?page=1",
"id" => "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes",
"totalItems" => 3,
"type" => "OrderedCollection"
}

new_data = Map.put(object2.data, "likes", likes)

object2
|> Ecto.Changeset.change(%{data: new_data})
|> Repo.update()

assert length(Object.get_by_id(object.id).data["likes"]) == 1
assert is_map(Object.get_by_id(object2.id).data["likes"])

assert :ok == Mix.Tasks.Pleroma.Database.run(["fix_likes_collections"])

assert length(Object.get_by_id(object.id).data["likes"]) == 1
assert Enum.empty?(Object.get_by_id(object2.id).data["likes"])
end
end
end

+ 32
- 0
test/uploaders/local_test.exs View File

@@ -0,0 +1,32 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Uploaders.LocalTest do
use Pleroma.DataCase
alias Pleroma.Uploaders.Local

describe "get_file/1" do
test "it returns path to local folder for files" do
assert Local.get_file("") == {:ok, {:static_dir, "test/uploads"}}
end
end

describe "put_file/1" do
test "put file to local folder" do
file_path = "local_upload/files/image.jpg"

file = %Pleroma.Upload{
name: "image.jpg",
content_type: "image/jpg",
path: file_path,
tempfile: Path.absname("test/fixtures/image_tmp.jpg")
}

assert Local.put_file(file) == :ok

assert Path.join([Local.upload_path(), file_path])
|> File.exists?()
end
end
end

+ 50
- 0
test/uploaders/mdii_test.exs View File

@@ -0,0 +1,50 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Uploaders.MDIITest do
use Pleroma.DataCase
alias Pleroma.Uploaders.MDII
import Tesla.Mock

describe "get_file/1" do
test "it returns path to local folder for files" do
assert MDII.get_file("") == {:ok, {:static_dir, "test/uploads"}}
end
end

describe "put_file/1" do
setup do
file_upload = %Pleroma.Upload{
name: "mdii-image.jpg",
content_type: "image/jpg",
path: "test_folder/mdii-image.jpg",
tempfile: Path.absname("test/fixtures/image_tmp.jpg")
}

[file_upload: file_upload]
end

test "save file", %{file_upload: file_upload} do
mock(fn
%{method: :post, url: "https://mdii.sakura.ne.jp/mdii-post.cgi?jpg"} ->
%Tesla.Env{status: 200, body: "mdii-image"}
end)

assert MDII.put_file(file_upload) ==
{:ok, {:url, "https://mdii.sakura.ne.jp/mdii-image.jpg"}}
end

test "save file to local if MDII isn`t available", %{file_upload: file_upload} do
mock(fn
%{method: :post, url: "https://mdii.sakura.ne.jp/mdii-post.cgi?jpg"} ->
%Tesla.Env{status: 500}
end)

assert MDII.put_file(file_upload) == :ok

assert Path.join([Pleroma.Uploaders.Local.upload_path(), file_upload.path])
|> File.exists?()
end
end
end

+ 90
- 0
test/uploaders/s3_test.exs View File

@@ -0,0 +1,90 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Uploaders.S3Test do
use Pleroma.DataCase

alias Pleroma.Config
alias Pleroma.Uploaders.S3

import Mock
import ExUnit.CaptureLog

setup do
config = Config.get([Pleroma.Uploaders.S3])

Config.put([Pleroma.Uploaders.S3],
bucket: "test_bucket",
public_endpoint: "https://s3.amazonaws.com"
)

on_exit(fn ->
Config.put([Pleroma.Uploaders.S3], config)
end)

:ok
end

describe "get_file/1" do
test "it returns path to local folder for files" do
assert S3.get_file("test_image.jpg") == {
:ok,
{:url, "https://s3.amazonaws.com/test_bucket/test_image.jpg"}
}
end

test "it returns path without bucket when truncated_namespace set to ''" do
Config.put([Pleroma.Uploaders.S3],
bucket: "test_bucket",
public_endpoint: "https://s3.amazonaws.com",
truncated_namespace: ""
)

assert S3.get_file("test_image.jpg") == {
:ok,
{:url, "https://s3.amazonaws.com/test_image.jpg"}
}
end

test "it returns path with bucket namespace when namespace is set" do
Config.put([Pleroma.Uploaders.S3],
bucket: "test_bucket",
public_endpoint: "https://s3.amazonaws.com",
bucket_namespace: "family"
)

assert S3.get_file("test_image.jpg") == {
:ok,
{:url, "https://s3.amazonaws.com/family:test_bucket/test_image.jpg"}
}
end
end

describe "put_file/1" do
setup do
file_upload = %Pleroma.Upload{
name: "image-tet.jpg",
content_type: "image/jpg",
path: "test_folder/image-tet.jpg",
tempfile: Path.absname("test/fixtures/image_tmp.jpg")
}

[file_upload: file_upload]
end

test "save file", %{file_upload: file_upload} do
with_mock ExAws, request: fn _ -> {:ok, :ok} end do
assert S3.put_file(file_upload) == {:ok, {:file, "test_folder/image-tet.jpg"}}
end
end

test "returns error", %{file_upload: file_upload} do
with_mock ExAws, request: fn _ -> {:error, "S3 Upload failed"} end do
assert capture_log(fn ->
assert S3.put_file(file_upload) == {:error, "S3 Upload failed"}
end) =~ "Elixir.Pleroma.Uploaders.S3: {:error, \"S3 Upload failed\"}"
end
end
end
end

+ 4
- 1
test/user_test.exs View File

@@ -525,7 +525,10 @@ defmodule Pleroma.UserTest do
end

test "it restricts some sizes" do
[bio: 5000, name: 100]
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)

[bio: bio_limit, name: name_limit]
|> Enum.each(fn {field, size} ->
string = String.pad_leading(".", size)
cs = User.remote_user_creation(Map.put(@valid_remote, field, string))


+ 45
- 5
test/web/activity_pub/mrf/mrf_test.exs View File

@@ -4,8 +4,8 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do

test "subdomains_regex/1" do
assert MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"]) == [
~r/^unsafe.tld$/,
~r/^(.*\.)*unsafe.tld$/
~r/^unsafe.tld$/i,
~r/^(.*\.)*unsafe.tld$/i
]
end

@@ -13,7 +13,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
test "common domains" do
regexes = MRF.subdomains_regex(["unsafe.tld", "unsafe2.tld"])

assert regexes == [~r/^unsafe.tld$/, ~r/^unsafe2.tld$/]
assert regexes == [~r/^unsafe.tld$/i, ~r/^unsafe2.tld$/i]

assert MRF.subdomain_match?(regexes, "unsafe.tld")
assert MRF.subdomain_match?(regexes, "unsafe2.tld")
@@ -24,7 +24,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
test "wildcard domains with one subdomain" do
regexes = MRF.subdomains_regex(["*.unsafe.tld"])

assert regexes == [~r/^(.*\.)*unsafe.tld$/]
assert regexes == [~r/^(.*\.)*unsafe.tld$/i]

assert MRF.subdomain_match?(regexes, "unsafe.tld")
assert MRF.subdomain_match?(regexes, "sub.unsafe.tld")
@@ -35,12 +35,52 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
test "wildcard domains with two subdomains" do
regexes = MRF.subdomains_regex(["*.unsafe.tld"])

assert regexes == [~r/^(.*\.)*unsafe.tld$/]
assert regexes == [~r/^(.*\.)*unsafe.tld$/i]

assert MRF.subdomain_match?(regexes, "unsafe.tld")
assert MRF.subdomain_match?(regexes, "sub.sub.unsafe.tld")
refute MRF.subdomain_match?(regexes, "sub.anotherunsafe.tld")
refute MRF.subdomain_match?(regexes, "sub.unsafe.tldanother")
end

test "matches are case-insensitive" do
regexes = MRF.subdomains_regex(["UnSafe.TLD", "UnSAFE2.Tld"])

assert regexes == [~r/^UnSafe.TLD$/i, ~r/^UnSAFE2.Tld$/i]

assert MRF.subdomain_match?(regexes, "UNSAFE.TLD")
assert MRF.subdomain_match?(regexes, "UNSAFE2.TLD")
assert MRF.subdomain_match?(regexes, "unsafe.tld")
assert MRF.subdomain_match?(regexes, "unsafe2.tld")

refute MRF.subdomain_match?(regexes, "EXAMPLE.COM")
refute MRF.subdomain_match?(regexes, "example.com")
end
end

describe "describe/0" do
test "it works as expected with noop policy" do
expected = %{
mrf_policies: ["NoOpPolicy"],
exclusions: false
}

{:ok, ^expected} = MRF.describe()
end

test "it works as expected with mock policy" do
config = Pleroma.Config.get([:instance, :rewrite_policy])
Pleroma.Config.put([:instance, :rewrite_policy], [MRFModuleMock])

expected = %{
mrf_policies: ["MRFModuleMock"],
mrf_module_mock: "some config data",
exclusions: false
}

{:ok, ^expected} = MRF.describe()

Pleroma.Config.put([:instance, :rewrite_policy], config)
end
end
end

+ 123
- 0
test/web/activity_pub/mrf/vocabulary_policy_test.exs View File

@@ -0,0 +1,123 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do
use Pleroma.DataCase

alias Pleroma.Web.ActivityPub.MRF.VocabularyPolicy

describe "accept" do
test "it accepts based on parent activity type" do
config = Pleroma.Config.get([:mrf_vocabulary, :accept])
Pleroma.Config.put([:mrf_vocabulary, :accept], ["Like"])

message = %{
"type" => "Like",
"object" => "whatever"
}

{:ok, ^message} = VocabularyPolicy.filter(message)

Pleroma.Config.put([:mrf_vocabulary, :accept], config)
end

test "it accepts based on child object type" do
config = Pleroma.Config.get([:mrf_vocabulary, :accept])
Pleroma.Config.put([:mrf_vocabulary, :accept], ["Create", "Note"])

message = %{
"type" => "Create",
"object" => %{
"type" => "Note",
"content" => "whatever"
}
}

{:ok, ^message} = VocabularyPolicy.filter(message)

Pleroma.Config.put([:mrf_vocabulary, :accept], config)
end

test "it does not accept disallowed child objects" do
config = Pleroma.Config.get([:mrf_vocabulary, :accept])
Pleroma.Config.put([:mrf_vocabulary, :accept], ["Create", "Note"])

message = %{
"type" => "Create",
"object" => %{
"type" => "Article",
"content" => "whatever"
}
}

{:reject, nil} = VocabularyPolicy.filter(message)

Pleroma.Config.put([:mrf_vocabulary, :accept], config)
end

test "it does not accept disallowed parent types" do
config = Pleroma.Config.get([:mrf_vocabulary, :accept])
Pleroma.Config.put([:mrf_vocabulary, :accept], ["Announce", "Note"])

message = %{
"type" => "Create",
"object" => %{
"type" => "Note",
"content" => "whatever"
}
}

{:reject, nil} = VocabularyPolicy.filter(message)

Pleroma.Config.put([:mrf_vocabulary, :accept], config)
end
end

describe "reject" do
test "it rejects based on parent activity type" do
config = Pleroma.Config.get([:mrf_vocabulary, :reject])
Pleroma.Config.put([:mrf_vocabulary, :reject], ["Like"])

message = %{
"type" => "Like",
"object" => "whatever"
}

{:reject, nil} = VocabularyPolicy.filter(message)

Pleroma.Config.put([:mrf_vocabulary, :reject], config)
end

test "it rejects based on child object type" do
config = Pleroma.Config.get([:mrf_vocabulary, :reject])
Pleroma.Config.put([:mrf_vocabulary, :reject], ["Note"])

message = %{
"type" => "Create",
"object" => %{
"type" => "Note",
"content" => "whatever"
}
}

{:reject, nil} = VocabularyPolicy.filter(message)

Pleroma.Config.put([:mrf_vocabulary, :reject], config)
end

test "it passes through objects that aren't disallowed" do
config = Pleroma.Config.get([:mrf_vocabulary, :reject])
Pleroma.Config.put([:mrf_vocabulary, :reject], ["Like"])

message = %{
"type" => "Announce",
"object" => "whatever"
}

{:ok, ^message} = VocabularyPolicy.filter(message)

Pleroma.Config.put([:mrf_vocabulary, :reject], config)
end
end
end

+ 61
- 1
test/web/activity_pub/relay_test.exs View File

@@ -5,11 +5,71 @@
defmodule Pleroma.Web.ActivityPub.RelayTest do
use Pleroma.DataCase

alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Relay

import Pleroma.Factory

test "gets an actor for the relay" do
user = Relay.get_actor()
assert user.ap_id == "#{Pleroma.Web.Endpoint.url()}/relay"
end

describe "follow/1" do
test "returns errors when user not found" do
assert Relay.follow("test-ap-id") == {:error, "Could not fetch by AP id"}
end

test "returns activity" do
user = insert(:user)
service_actor = Relay.get_actor()
assert {:ok, %Activity{} = activity} = Relay.follow(user.ap_id)
assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay"
assert user.ap_id in activity.recipients
assert activity.data["type"] == "Follow"
assert activity.data["actor"] == service_actor.ap_id
assert activity.data["object"] == user.ap_id
end
end

describe "unfollow/1" do
test "returns errors when user not found" do
assert Relay.unfollow("test-ap-id") == {:error, "Could not fetch by AP id"}
end

test "returns activity" do
user = insert(:user)
service_actor = Relay.get_actor()
ActivityPub.follow(service_actor, user)
assert {:ok, %Activity{} = activity} = Relay.unfollow(user.ap_id)
assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay"
assert user.ap_id in activity.recipients
assert activity.data["type"] == "Undo"
assert activity.data["actor"] == service_actor.ap_id
assert activity.data["to"] == [user.ap_id]
end
end

describe "publish/1" do
test "returns error when activity not `Create` type" do
activity = insert(:like_activity)
assert Relay.publish(activity) == {:error, "Not implemented"}
end

test "returns error when activity not public" do
activity = insert(:direct_note_activity)
assert Relay.publish(activity) == {:error, false}
end

assert user.ap_id =~ "/relay"
test "returns announce activity" do
service_actor = Relay.get_actor()
note = insert(:note_activity)
assert {:ok, %Activity{} = activity, %Object{} = obj} = Relay.publish(note)
assert activity.data["type"] == "Announce"
assert activity.data["actor"] == service_actor.ap_id
assert activity.data["object"] == obj.data["id"]
end
end
end

+ 22
- 8
test/web/activity_pub/transmogrifier_test.exs View File

@@ -450,6 +450,27 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert !is_nil(data["cc"])
end

test "it strips internal likes" do
data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Poison.decode!()

likes = %{
"first" =>
"http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes?page=1",
"id" => "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes",
"totalItems" => 3,
"type" => "OrderedCollection"
}

object = Map.put(data["object"], "likes", likes)
data = Map.put(data, "object", object)

{:ok, %Activity{object: object}} = Transmogrifier.handle_incoming(data)

refute Map.has_key?(object.data, "likes")
end

test "it works for incoming update activities" do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()

@@ -1061,14 +1082,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert is_nil(modified["object"]["announcements"])
assert is_nil(modified["object"]["announcement_count"])
assert is_nil(modified["object"]["context_id"])
end

test "it adds like collection to object" do
activity = insert(:note_activity)
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)

assert modified["object"]["likes"]["type"] == "OrderedCollection"
assert modified["object"]["likes"]["totalItems"] == 0
assert is_nil(modified["object"]["likes"])
end

test "the directMessage flag is present" do


+ 27
- 0
test/web/mastodon_api/account_view_test.exs View File

@@ -356,4 +356,31 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
result = AccountView.render("account.json", %{user: user})
refute result.display_name == "<marquee> username </marquee>"
end

describe "hiding follows/following" do
test "shows when follows/following are hidden and sets follower/following count to 0" do
user = insert(:user, info: %{hide_followers: true, hide_follows: true})
other_user = insert(:user)
{:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user)
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)

assert %{
followers_count: 0,
following_count: 0,
pleroma: %{hide_follows: true, hide_followers: true}
} = AccountView.render("account.json", %{user: user})
end

test "shows actual follower/following count to the account owner" do
user = insert(:user, info: %{hide_followers: true, hide_follows: true})
other_user = insert(:user)
{:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user)
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)

assert %{
followers_count: 1,
following_count: 1
} = AccountView.render("account.json", %{user: user, for: user})
end
end
end

+ 114
- 31
test/web/mastodon_api/mastodon_api_controller_test.exs View File

@@ -7,6 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do

alias Ecto.Changeset
alias Pleroma.Activity
alias Pleroma.Config
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Repo
@@ -85,11 +86,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
end

test "the public timeline when public is set to false", %{conn: conn} do
public = Pleroma.Config.get([:instance, :public])
Pleroma.Config.put([:instance, :public], false)
public = Config.get([:instance, :public])
Config.put([:instance, :public], false)

on_exit(fn ->
Pleroma.Config.put([:instance, :public], public)
Config.put([:instance, :public], public)
end)

assert conn
@@ -250,7 +251,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
end

test "posting a status with OGP link preview", %{conn: conn} do
Pleroma.Config.put([:rich_media, :enabled], true)
Config.put([:rich_media, :enabled], true)

conn =
conn
@@ -260,7 +261,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do

assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
assert Activity.get_by_id(id)
Pleroma.Config.put([:rich_media, :enabled], false)
Config.put([:rich_media, :enabled], false)
end

test "posting a direct status", %{conn: conn} do
@@ -304,7 +305,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do

test "option limit is enforced", %{conn: conn} do
user = insert(:user)
limit = Pleroma.Config.get([:instance, :poll_limits, :max_options])
limit = Config.get([:instance, :poll_limits, :max_options])

conn =
conn
@@ -320,7 +321,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do

test "option character limit is enforced", %{conn: conn} do
user = insert(:user)
limit = Pleroma.Config.get([:instance, :poll_limits, :max_option_chars])
limit = Config.get([:instance, :poll_limits, :max_option_chars])

conn =
conn
@@ -339,7 +340,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do

test "minimal date limit is enforced", %{conn: conn} do
user = insert(:user)
limit = Pleroma.Config.get([:instance, :poll_limits, :min_expiration])
limit = Config.get([:instance, :poll_limits, :min_expiration])

conn =
conn
@@ -358,7 +359,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do

test "maximum date limit is enforced", %{conn: conn} do
user = insert(:user)
limit = Pleroma.Config.get([:instance, :poll_limits, :max_expiration])
limit = Config.get([:instance, :poll_limits, :max_expiration])

conn =
conn
@@ -1633,12 +1634,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do

describe "media upload" do
setup do
upload_config = Pleroma.Config.get([Pleroma.Upload])
proxy_config = Pleroma.Config.get([:media_proxy])
upload_config = Config.get([Pleroma.Upload])
proxy_config = Config.get([:media_proxy])

on_exit(fn ->
Pleroma.Config.put([Pleroma.Upload], upload_config)
Pleroma.Config.put([:media_proxy], proxy_config)
Config.put([Pleroma.Upload], upload_config)
Config.put([:media_proxy], proxy_config)
end)

user = insert(:user)
@@ -2581,7 +2582,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
conn = get(conn, "/api/v1/instance")
assert result = json_response(conn, 200)

email = Pleroma.Config.get([:instance, :email])
email = Config.get([:instance, :email])
# Note: not checking for "max_toot_chars" since it's optional
assert %{
"uri" => _,
@@ -2623,7 +2624,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|> Changeset.put_embed(:info, info_change)
|> User.update_and_set_cache()

Pleroma.Stats.update_stats()
Pleroma.Stats.force_update()

conn = get(conn, "/api/v1/instance")

@@ -2641,7 +2642,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
insert(:user, %{local: false, nickname: "u@peer1.com"})
insert(:user, %{local: false, nickname: "u@peer2.com"})

Pleroma.Stats.update_stats()
Pleroma.Stats.force_update()

conn = get(conn, "/api/v1/instance/peers")

@@ -2666,7 +2667,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do

describe "pinned statuses" do
setup do
Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
Config.put([:instance, :max_pinned_statuses], 1)

user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
@@ -2766,10 +2767,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do

describe "cards" do
setup do
Pleroma.Config.put([:rich_media, :enabled], true)
Config.put([:rich_media, :enabled], true)

on_exit(fn ->
Pleroma.Config.put([:rich_media, :enabled], false)
Config.put([:rich_media, :enabled], false)
end)

user = insert(:user)
@@ -2901,8 +2902,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do

describe "conversation muting" do
setup do
post_user = insert(:user)
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})

{:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})

[user: user, activity: activity]
end
@@ -2995,7 +2998,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
reporter: reporter,
target_user: target_user
} do
max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
max_size = Config.get([:instance, :max_report_comment_size], 1000)
comment = String.pad_trailing("a", max_size + 1, "a")

error = %{"error" => "Comment must be up to #{max_size} characters"}
@@ -3124,15 +3127,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
conn: conn,
path: path
} do
is_public = Pleroma.Config.get([:instance, :public])
Pleroma.Config.put([:instance, :public], false)
is_public = Config.get([:instance, :public])
Config.put([:instance, :public], false)

conn = get(conn, path)

assert conn.status == 302
assert redirected_to(conn) == "/web/login"

Pleroma.Config.put([:instance, :public], is_public)
Config.put([:instance, :public], is_public)
end

test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
@@ -3874,8 +3877,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)

email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
notify_email = Pleroma.Config.get([:instance, :notify_email])
instance_name = Pleroma.Config.get([:instance, :name])
notify_email = Config.get([:instance, :notify_email])
instance_name = Config.get([:instance, :name])

assert_email_sent(
from: {instance_name, notify_email},
@@ -3907,11 +3910,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do

describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
setup do
setting = Pleroma.Config.get([:instance, :account_activation_required])
setting = Config.get([:instance, :account_activation_required])

unless setting do
Pleroma.Config.put([:instance, :account_activation_required], true)
on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end)
Config.put([:instance, :account_activation_required], true)
on_exit(fn -> Config.put([:instance, :account_activation_required], setting) end)
end

user = insert(:user)
@@ -3935,8 +3938,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|> json_response(:no_content)

email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
notify_email = Pleroma.Config.get([:instance, :notify_email])
instance_name = Pleroma.Config.get([:instance, :name])
notify_email = Config.get([:instance, :notify_email])
instance_name = Config.get([:instance, :name])

assert_email_sent(
from: {instance_name, notify_email},
@@ -3945,4 +3948,84 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
)
end
end

describe "GET /api/v1/suggestions" do
setup do
user = insert(:user)
other_user = insert(:user)
config = Config.get(:suggestions)
on_exit(fn -> Config.put(:suggestions, config) end)

host = Config.get([Pleroma.Web.Endpoint, :url, :host])
url500 = "http://test500?#{host}&#{user.nickname}"
url200 = "http://test200?#{host}&#{user.nickname}"

mock(fn
%{method: :get, url: ^url500} ->
%Tesla.Env{status: 500, body: "bad request"}

%{method: :get, url: ^url200} ->
%Tesla.Env{
status: 200,
body:
~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
other_user.ap_id
}","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
}
end)

[user: user, other_user: other_user]
end

test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
Config.put([:suggestions, :enabled], false)

res =
conn
|> assign(:user, user)
|> get("/api/v1/suggestions")
|> json_response(200)

assert res == []
end

test "returns error", %{conn: conn, user: user} do
Config.put([:suggestions, :enabled], true)
Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")

res =
conn
|> assign(:user, user)
|> get("/api/v1/suggestions")
|> json_response(500)

assert res == "Something went wrong"
end

test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
Config.put([:suggestions, :enabled], true)
Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")

res =
conn
|> assign(:user, user)
|> get("/api/v1/suggestions")
|> json_response(200)

assert res == [
%{
"acct" => "yj455",
"avatar" => "https://social.heldscal.la/avatar/201.jpeg",
"avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
"id" => 0
},
%{
"acct" => other_user.ap_id,
"avatar" => "https://social.heldscal.la/avatar/202.jpeg",
"avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
"id" => other_user.id
}
]
end
end
end

+ 103
- 0
test/web/mastodon_api/mastodon_api_test.exs View File

@@ -0,0 +1,103 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do
use Pleroma.Web.ConnCase

alias Pleroma.Notification
alias Pleroma.ScheduledActivity
alias Pleroma.User
alias Pleroma.Web.MastodonAPI.MastodonAPI
alias Pleroma.Web.TwitterAPI.TwitterAPI

import Pleroma.Factory

describe "follow/3" do
test "returns error when user deactivated" do
follower = insert(:user)
user = insert(:user, local: true, info: %{deactivated: true})
{:error, error} = MastodonAPI.follow(follower, user)
assert error == "Could not follow user: You are deactivated."
end

test "following for user" do
follower = insert(:user)
user = insert(:user)
{:ok, follower} = MastodonAPI.follow(follower, user)
assert User.following?(follower, user)
end

test "returns ok if user already followed" do
follower = insert(:user)
user = insert(:user)
{:ok, follower} = User.follow(follower, user)
{:ok, follower} = MastodonAPI.follow(follower, refresh_record(user))
assert User.following?(follower, user)
end
end

describe "get_followers/2" do
test "returns user followers" do
follower1_user = insert(:user)
follower2_user = insert(:user)
user = insert(:user)
{:ok, _follower1_user} = User.follow(follower1_user, user)
{:ok, follower2_user} = User.follow(follower2_user, user)

assert MastodonAPI.get_followers(user, %{"limit" => 1}) == [follower2_user]
end
end

describe "get_friends/2" do
test "returns user friends" do
user = insert(:user)
followed_one = insert(:user)
followed_two = insert(:user)
followed_three = insert(:user)

{:ok, user} = User.follow(user, followed_one)
{:ok, user} = User.follow(user, followed_two)
{:ok, user} = User.follow(user, followed_three)
res = MastodonAPI.get_friends(user)

assert length(res) == 3
assert Enum.member?(res, refresh_record(followed_three))
assert Enum.member?(res, refresh_record(followed_two))
assert Enum.member?(res, refresh_record(followed_one))
end
end

describe "get_notifications/2" do
test "returns notifications for user" do
user = insert(:user)
subscriber = insert(:user)

User.subscribe(subscriber, user)

{:ok, status} = TwitterAPI.create_status(user, %{"status" => "Akariiiin"})
{:ok, status1} = TwitterAPI.create_status(user, %{"status" => "Magi"})
{:ok, [notification]} = Notification.create_notifications(status)
{:ok, [notification1]} = Notification.create_notifications(status1)
res = MastodonAPI.get_notifications(subscriber)

assert Enum.member?(Enum.map(res, & &1.id), notification.id)
assert Enum.member?(Enum.map(res, & &1.id), notification1.id)
end
end

describe "get_scheduled_activities/2" do
test "returns user scheduled activities" do
user = insert(:user)

today =
NaiveDateTime.utc_now()
|> NaiveDateTime.add(:timer.minutes(6), :millisecond)
|> NaiveDateTime.to_iso8601()

attrs = %{params: %{}, scheduled_at: today}
{:ok, schedule} = ScheduledActivity.create(user, attrs)
assert MastodonAPI.get_scheduled_activities(user) == [schedule]
end
end
end

+ 8
- 0
test/web/node_info_test.exs View File

@@ -85,6 +85,9 @@ defmodule Pleroma.Web.NodeInfoTest do
end

test "it shows MRF transparency data if enabled", %{conn: conn} do
config = Pleroma.Config.get([:instance, :rewrite_policy])
Pleroma.Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])

option = Pleroma.Config.get([:instance, :mrf_transparency])
Pleroma.Config.put([:instance, :mrf_transparency], true)

@@ -98,11 +101,15 @@ defmodule Pleroma.Web.NodeInfoTest do

assert response["metadata"]["federation"]["mrf_simple"] == simple_config

Pleroma.Config.put([:instance, :rewrite_policy], config)
Pleroma.Config.put([:instance, :mrf_transparency], option)
Pleroma.Config.put(:mrf_simple, %{})
end

test "it performs exclusions from MRF transparency data if configured", %{conn: conn} do
config = Pleroma.Config.get([:instance, :rewrite_policy])
Pleroma.Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])

option = Pleroma.Config.get([:instance, :mrf_transparency])
Pleroma.Config.put([:instance, :mrf_transparency], true)

@@ -122,6 +129,7 @@ defmodule Pleroma.Web.NodeInfoTest do
assert response["metadata"]["federation"]["mrf_simple"] == expected_config
assert response["metadata"]["federation"]["exclusions"] == true

Pleroma.Config.put([:instance, :rewrite_policy], config)
Pleroma.Config.put([:instance, :mrf_transparency], option)
Pleroma.Config.put([:instance, :mrf_transparency_exclusions], exclusions)
Pleroma.Config.put(:mrf_simple, %{})


Loading…
Cancel
Save