merge develop
authorRoman Chvanikov <chvanikoff@pm.me>
Fri, 12 Jul 2019 15:08:27 +0000 (18:08 +0300)
committerRoman Chvanikov <chvanikoff@pm.me>
Fri, 12 Jul 2019 15:08:27 +0000 (18:08 +0300)
160 files changed:
.mailmap [new file with mode: 0644]
CHANGELOG.md
config/config.exs
config/test.exs
docs/api/admin_api.md
docs/api/differences_in_mastoapi_responses.md
docs/config.md
lib/healthcheck.ex
lib/jason_types.ex
lib/mix/tasks/pleroma/benchmark.ex
lib/mix/tasks/pleroma/config.ex
lib/mix/tasks/pleroma/instance.ex
lib/pleroma/activity.ex
lib/pleroma/activity/search.ex
lib/pleroma/application.ex
lib/pleroma/bbs/authenticator.ex
lib/pleroma/bbs/handler.ex
lib/pleroma/bookmark.ex
lib/pleroma/captcha/captcha.ex
lib/pleroma/captcha/kocaptcha.ex
lib/pleroma/config/transfer_task.ex
lib/pleroma/emails/mailer.ex
lib/pleroma/instances.ex
lib/pleroma/instances/instance.ex
lib/pleroma/object.ex
lib/pleroma/object/fetcher.ex
lib/pleroma/object_tombstone.ex
lib/pleroma/pagination.ex
lib/pleroma/plugs/ensure_authenticated_plug.ex
lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex
lib/pleroma/plugs/oauth_scopes_plug.ex
lib/pleroma/plugs/rate_limiter.ex
lib/pleroma/plugs/set_locale_plug.ex [new file with mode: 0644]
lib/pleroma/plugs/uploaded_media.ex
lib/pleroma/plugs/user_is_admin_plug.ex
lib/pleroma/reverse_proxy/client.ex
lib/pleroma/uploaders/uploader.ex
lib/pleroma/user.ex
lib/pleroma/user/search.ex
lib/pleroma/user/synchronization.ex [deleted file]
lib/pleroma/user/synchronization_worker.ex [deleted file]
lib/pleroma/user/welcome_message.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/activity_pub/activity_pub_controller.ex
lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex
lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex
lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
lib/pleroma/web/activity_pub/mrf/tag_policy.ex
lib/pleroma/web/activity_pub/mrf/user_allowlist_policy.ex [moved from lib/pleroma/web/activity_pub/mrf/user_allowlist.ex with 86% similarity]
lib/pleroma/web/activity_pub/transmogrifier.ex
lib/pleroma/web/activity_pub/visibility.ex
lib/pleroma/web/admin_api/admin_api_controller.ex
lib/pleroma/web/admin_api/config.ex
lib/pleroma/web/admin_api/views/config_view.ex
lib/pleroma/web/common_api/common_api.ex
lib/pleroma/web/common_api/utils.ex
lib/pleroma/web/endpoint.ex
lib/pleroma/web/mastodon_api/mastodon_api.ex
lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
lib/pleroma/web/mastodon_api/search_controller.ex
lib/pleroma/web/mastodon_api/subscription_controller.ex
lib/pleroma/web/mastodon_api/views/conversation_view.ex
lib/pleroma/web/mastodon_api/views/status_view.ex
lib/pleroma/web/metadata/player_view.ex
lib/pleroma/web/metadata/rel_me.ex
lib/pleroma/web/mongooseim/mongoose_im_controller.ex
lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
lib/pleroma/web/oauth/fallback_controller.ex
lib/pleroma/web/oauth/oauth_controller.ex
lib/pleroma/web/oauth/token/response.ex
lib/pleroma/web/oauth/token/strategy/refresh_token.ex
lib/pleroma/web/oauth/token/strategy/revoke.ex
lib/pleroma/web/oauth/token/utils.ex
lib/pleroma/web/ostatus/ostatus_controller.ex
lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex
lib/pleroma/web/rich_media/parsers/oembed_parser.ex
lib/pleroma/web/rich_media/parsers/ogp.ex
lib/pleroma/web/rich_media/parsers/twitter_card.ex
lib/pleroma/web/translation_helpers.ex [new file with mode: 0644]
lib/pleroma/web/uploader_controller.ex
lib/pleroma/web/web.ex
lib/transports.ex
lib/xml_builder.ex
mix.exs
mix.lock
priv/gettext/en/LC_MESSAGES/errors.po
priv/gettext/errors.pot
priv/gettext/ru/LC_MESSAGES/errors.po [new file with mode: 0644]
priv/repo/migrations/20190710115833_add_following_address_to_user.exs [new file with mode: 0644]
priv/repo/migrations/20190710125051_add_following_address_index_to_user.exs [new file with mode: 0644]
priv/repo/migrations/20190710125158_add_following_address_from_source_data.exs [new file with mode: 0644]
priv/templates/sample_config.eex
test/activity_test.exs
test/bbs/handler_test.exs
test/bookmark_test.exs
test/config/transfer_task_test.exs
test/emails/admin_email_test.exs [new file with mode: 0644]
test/emails/mailer_test.exs [new file with mode: 0644]
test/emails/user_email_test.exs [new file with mode: 0644]
test/emoji_test.exs
test/fixtures/host-meta-zetsubou.xn--q9jyb4c.xml [new file with mode: 0644]
test/fixtures/lain.xml [new file with mode: 0644]
test/fixtures/mastodon-delete-user.json [new file with mode: 0644]
test/healthcheck_test.exs
test/http/request_builder_test.exs
test/keys_test.exs
test/object/containment_test.exs
test/object/fetcher_test.exs
test/plugs/rate_limiter_test.exs
test/plugs/set_locale_plug_test.exs [new file with mode: 0644]
test/repo_test.exs
test/reverse_proxy_test.exs
test/support/factory.ex
test/support/http_request_mock.ex
test/tasks/config_test.exs
test/tasks/ecto/ecto_test.exs
test/tasks/ecto/rollback_test.exs
test/tasks/instance_test.exs [moved from test/tasks/instance.exs with 78% similarity]
test/tasks/pleroma_test.exs
test/tasks/robots_txt_test.exs
test/upload/filter/anonymize_filename_test.exs
test/user/synchronization_test.exs [deleted file]
test/user/synchronization_worker_test.exs [deleted file]
test/user_invite_token_test.exs
test/user_search_test.exs
test/user_test.exs
test/web/activity_pub/activity_pub_controller_test.exs
test/web/activity_pub/activity_pub_test.exs
test/web/activity_pub/mrf/ensure_re_prepended_test.exs [new file with mode: 0644]
test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs [new file with mode: 0644]
test/web/activity_pub/mrf/normalize_markup_test.exs [new file with mode: 0644]
test/web/activity_pub/mrf/reject_non_public_test.exs [new file with mode: 0644]
test/web/activity_pub/mrf/tag_policy_test.exs [new file with mode: 0644]
test/web/activity_pub/mrf/user_allowlist_policy_test.exs [new file with mode: 0644]
test/web/activity_pub/transmogrifier_test.exs
test/web/activity_pub/utils_test.exs
test/web/activity_pub/views/object_view_test.exs
test/web/activity_pub/views/user_view_test.exs
test/web/activity_pub/visibilty_test.exs
test/web/admin_api/admin_api_controller_test.exs
test/web/admin_api/config_test.exs
test/web/common_api/common_api_test.exs
test/web/mastodon_api/mastodon_api_controller_test.exs
test/web/mastodon_api/search_controller_test.exs
test/web/mastodon_api/status_view_test.exs
test/web/metadata/rel_me_test.exs
test/web/ostatus/activity_representer_test.exs
test/web/ostatus/incoming_documents/delete_handling_test.exs
test/web/ostatus/ostatus_controller_test.exs
test/web/ostatus/ostatus_test.exs
test/web/rel_me_test.exs
test/web/rich_media/helpers_test.exs
test/web/rich_media/parser_test.exs
test/web/twitter_api/password_controller_test.exs
test/web/twitter_api/twitter_api_controller_test.exs
test/web/twitter_api/twitter_api_test.exs
test/web/twitter_api/util_controller_test.exs
test/web/twitter_api/views/activity_view_test.exs
test/web/web_finger/web_finger_test.exs

diff --git a/.mailmap b/.mailmap
new file mode 100644 (file)
index 0000000..e4ca5f9
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,2 @@
+Ariadne Conill <ariadne@dereferenced.org> <nenolod@dereferenced.org>
+Ariadne Conill <ariadne@dereferenced.org> <nenolod@gmail.com>
index f486e01ac0349cdf4a5ba9f841d592821ba120a4..5e703b1fd13efc2910222bd0dfe5e6158ad3f7c8 100644 (file)
@@ -4,28 +4,36 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ## [Unreleased]
+### Changed
+- **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config
+- Configuration: OpenGraph and TwitterCard providers enabled by default
+- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
+- NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
+
+### Fixed
+- Not being able to pin unlisted posts
+- Metadata rendering errors resulting in the entire page being inaccessible
+- 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: Add `account_id`, `type`, `offset`, and `limit` to search API (`/api/v1/search` and `/api/v2/search`)
+
 ### Added
 - MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
 Configuration: `federation_incoming_replies_max_depth` option
 - Mastodon API: Support for the [`tagged` filter](https://github.com/tootsuite/mastodon/pull/9755) in [`GET /api/v1/accounts/:id/statuses`](https://docs.joinmastodon.org/api/rest/accounts/#get-api-v1-accounts-id-statuses)
 - Mastodon API, streaming: Add support for passing the token in the `Sec-WebSocket-Protocol` header
+- Mastodon API, extension: Ability to reset avatar, profile banner, and background
 - Admin API: Return users' tags when querying reports
 - Admin API: Return avatar and display name when querying users
 - Admin API: Allow querying user by ID
+- Admin API: Added support for `tuples`.
 - Added synchronization of following/followers counters for external users
-
-### Fixed
-- Not being able to pin unlisted posts
-- Metadata rendering errors resulting in the entire page being inaccessible
-- 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
+- Configuration: `enabled` option for `Pleroma.Emails.Mailer`, defaulting to `false`.
+- Mastodon API: Add support for categories for custom emojis by reusing the group feature. <https://github.com/tootsuite/mastodon/pull/11196>
 
 ### Changed
-- Configuration: OpenGraph and TwitterCard providers enabled by default
 - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
-
-### Changed
-- NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
+- Admin API: changed json structure for saving config settings.
 
 ## [1.0.0] - 2019-06-29
 ### Security
@@ -88,7 +96,6 @@ Configuration: `federation_incoming_replies_max_depth` option
 - OAuth: added job to clean expired access tokens
 - MRF: Support for rejecting reports from specific instances (`mrf_simple`)
 - MRF: Support for stripping avatars and banner images from specific instances (`mrf_simple`)
-- Ability to reset avatar, profile banner and backgroud
 - MRF: Support for running subchains.
 - Configuration: `skip_thread_containment` option
 - Configuration: `rate_limit` option. See `Pleroma.Plugs.RateLimiter` documentation for details.
index d8c8b1a6d385a34d312253a3d784cb40b851f44e..a1a510a65c72d431b8d282bee273ccf8b38ed85f 100644 (file)
@@ -250,13 +250,7 @@ config :pleroma, :instance,
   skip_thread_containment: true,
   limit_to_local_content: :unauthenticated,
   dynamic_configuration: false,
-  external_user_synchronization: [
-    enabled: false,
-    # every 2 hours
-    interval: 60 * 60 * 2,
-    max_retries: 3,
-    limit: 500
-  ]
+  external_user_synchronization: true
 
 config :pleroma, :markup,
   # XXX - unfortunately, inline images must be enabled by default right now, because
@@ -501,7 +495,7 @@ config :ueberauth,
 
 config :pleroma, :auth, oauth_consumer_strategies: oauth_consumer_strategies
 
-config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Sendmail
+config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Sendmail, enabled: false
 
 config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics"
 
index 1635a5d92feaf8c5b114ba62dc478457af2a315a..e729d963021b3f2fec8da95ca1225e429c22688b 100644 (file)
@@ -23,7 +23,7 @@ config :pleroma, Pleroma.Upload, filters: [], link_name: false
 
 config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
 
-config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Test
+config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Test, enabled: true
 
 config :pleroma, :instance,
   email: "admin@example.com",
@@ -65,7 +65,9 @@ config :pleroma, Pleroma.ScheduledActivity,
   total_user_limit: 3,
   enabled: false
 
-config :pleroma, :rate_limit, app_account_creation: {10_000, 5}
+config :pleroma, :rate_limit,
+  search: [{1000, 30}, {1000, 30}],
+  app_account_creation: {10_000, 5}
 
 config :pleroma, :http_security, report_uri: "https://endpoint.com"
 
index bce5e399bbedecc806f9c50f7171a108dade74c3..c429da822860988db7e8bffdd518d0f729a4dc34 100644 (file)
@@ -573,7 +573,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
   configs: [
     {
       "group": string,
-      "key": string,
+      "key": string or string with leading `:` for atoms,
       "value": string or {} or [] or {"tuple": []}
      }
   ]
@@ -583,10 +583,11 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
 ## `/api/pleroma/admin/config`
 ### Update config settings
 Module name can be passed as string, which starts with `Pleroma`, e.g. `"Pleroma.Upload"`.
-Atom or boolean value can be passed with `:` in the beginning, e.g. `":true"`, `":upload"`. For keys it is not needed.
-Integer with `i:`, e.g. `"i:150"`.
-Tuple with more than 2 values with `{"tuple": ["first_val", Pleroma.Module, []]}`.
+Atom keys and values can be passed with `:` in the beginning, e.g. `":upload"`.
+Tuples can be passed as `{"tuple": ["first_val", Pleroma.Module, []]}`.
 `{"tuple": ["some_string", "Pleroma.Some.Module", []]}` will be converted to `{"some_string", Pleroma.Some.Module, []}`.
+Keywords can be passed as lists with 2 child tuples, e.g.
+`[{"tuple": ["first_val", Pleroma.Module]}, {"tuple": ["second_val", true]}]`.
 
 Compile time settings (need instance reboot):
 - all settings by this keys:
@@ -603,7 +604,7 @@ Compile time settings (need instance reboot):
 - Params:
   - `configs` => [
     - `group` (string)
-    - `key` (string)
+    - `key` (string or string with leading `:` for atoms)
     - `value` (string, [], {} or {"tuple": []})
     - `delete` = true (optional, if parameter must be deleted)
   ]
@@ -616,24 +617,25 @@ Compile time settings (need instance reboot):
     {
       "group": "pleroma",
       "key": "Pleroma.Upload",
-      "value": {
-        "uploader": "Pleroma.Uploaders.Local",
-        "filters": ["Pleroma.Upload.Filter.Dedupe"],
-        "link_name": ":true",
-        "proxy_remote": ":false",
-        "proxy_opts": {
-          "redirect_on_failure": ":false",
-          "max_body_length": "i:1048576",
-          "http": {
-            "follow_redirect": ":true",
-            "pool": ":upload"
-          }
-        },
-        "dispatch": {
+      "value": [
+        {"tuple": [":uploader", "Pleroma.Uploaders.Local"]},
+        {"tuple": [":filters", ["Pleroma.Upload.Filter.Dedupe"]]},
+        {"tuple": [":link_name", true]},
+        {"tuple": [":proxy_remote", false]},
+        {"tuple": [":proxy_opts", [
+          {"tuple": [":redirect_on_failure", false]},
+          {"tuple": [":max_body_length", 1048576]},
+          {"tuple": [":http": [
+            {"tuple": [":follow_redirect", true]},
+            {"tuple": [":pool", ":upload"]},
+          ]]}
+        ]
+        ]},
+        {"tuple": [":dispatch", {
           "tuple": ["/api/v1/streaming", "Pleroma.Web.MastodonAPI.WebsocketHandler", []]
-        }
-      }
-     }
+        }]}
+      ]
+    }
   ]
 }
 
@@ -644,7 +646,7 @@ Compile time settings (need instance reboot):
   configs: [
     {
       "group": string,
-      "key": string,
+      "key": string or string with leading `:` for atoms,
       "value": string or {} or [] or {"tuple": []}
      }
   ]
index 3ee7115cf5b696c58848eb45dc1989ce0498b3b2..2cbe1458dfe768dc5b799cc3c0c93a2a4944884d 100644 (file)
@@ -46,6 +46,14 @@ Has these additional fields under the `pleroma` object:
 - `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`
 - `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials`
 
+### Extensions for PleromaFE
+
+These endpoints added for controlling PleromaFE features over the Mastodon API
+
+- PATCH `/api/v1/accounts/update_avatar`: Set/clear user avatar image
+- PATCH `/api/v1/accounts/update_banner`: Set/clear user banner image
+- PATCH `/api/v1/accounts/update_background`: Set/clear user background image
+
 ### Source
 
 Has these additional fields under the `pleroma` object:
index cbaa790d1bcc325b0befcca672594b7000cfe2d6..4e0e9ac112d66e7d7fb0ff88249fa4901cbbda1d 100644 (file)
@@ -41,6 +41,7 @@ This filter replaces the filename (not the path) of an upload. For complete obfu
 ## Pleroma.Emails.Mailer
 * `adapter`: one of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters), or `Swoosh.Adapters.Local` for in-memory mailbox.
 * `api_key` / `password` and / or other adapter-specific settings, per the above documentation.
+* `enabled`: Allows enable/disable send  emails. Default: `false`.
 
 An example for Sendgrid adapter:
 
@@ -125,11 +126,7 @@ config :pleroma, Pleroma.Emails.Mailer,
 * `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.
-* `external_user_synchronization`: Following/followers counters synchronization settings.
-  * `enabled`: Enables synchronization
-  * `interval`: Interval between synchronization.
-  * `max_retries`: Max rettries for host. After exceeding the limit, the check will not be carried out for users from this host.
-  * `limit`: Users batch size for processing in one time.
+* `external_user_synchronization`: Enabling following/followers counters synchronization for external users.
 
 
 
index 32aafc2109bac0278b10bafc6ea4ada0d6b64244..f97d14432b27d1858e0df990d71b22b5b10b0519 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Healthcheck do
   @moduledoc """
   Module collects metrics about app and assign healthy status.
index d1a7bc7ac352b692dcc2a4f593a71b98eb85ab3f..c558aef57a0210b4c5bf4e032321703fdc319a9b 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 Postgrex.Types.define(
   Pleroma.PostgresTypes,
   [] ++ Ecto.Adapters.Postgres.extensions(),
index d43db7b35b1591537e0ef3f0d6d4d2ffe8a3328b..5222cce8022f97f328c678a8a4f2350f733b1cc2 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Mix.Tasks.Pleroma.Benchmark do
   import Mix.Pleroma
   use Mix.Task
index faa605d9bf091dbd61c02f3c21ee3247e2770484..a71bcd447bb648a4e9b9649273811910f4a1459e 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Mix.Tasks.Pleroma.Config do
   use Mix.Task
   import Mix.Pleroma
index f08e5ff3fe15f675024b595578c52a2ce46d6d99..b9b1991c29d6c4c153a6ec8008dc204fd18a9b55 100644 (file)
@@ -34,6 +34,8 @@ defmodule Mix.Tasks.Pleroma.Instance do
   - `--db-configurable Y/N` - Allow/disallow configuring instance from admin part
   - `--uploads-dir` - the directory uploads go in when using a local uploader
   - `--static-dir` - the directory custom public files should be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)
+  - `--listen-ip` - the ip the app should listen to, defaults to 127.0.0.1
+  - `--listen-port` - the port the app should listen to, defaults to 4000
   """
 
   def run(["gen" | rest]) do
@@ -56,7 +58,9 @@ defmodule Mix.Tasks.Pleroma.Instance do
           indexable: :string,
           db_configurable: :string,
           uploads_dir: :string,
-          static_dir: :string
+          static_dir: :string,
+          listen_ip: :string,
+          listen_port: :string
         ],
         aliases: [
           o: :output,
@@ -146,6 +150,22 @@ defmodule Mix.Tasks.Pleroma.Instance do
           "n"
         ) === "y"
 
+      listen_port =
+        get_option(
+          options,
+          :listen_port,
+          "What port will the app listen to (leave it if you are using the default setup with nginx)?",
+          4000
+        )
+
+      listen_ip =
+        get_option(
+          options,
+          :listen_ip,
+          "What ip will the app listen to (leave it if you are using the default setup with nginx)?",
+          "127.0.0.1"
+        )
+
       uploads_dir =
         get_option(
           options,
@@ -188,7 +208,9 @@ defmodule Mix.Tasks.Pleroma.Instance do
           db_configurable?: db_configurable?,
           static_dir: static_dir,
           uploads_dir: uploads_dir,
-          rum_enabled: rum_enabled
+          rum_enabled: rum_enabled,
+          listen_ip: listen_ip,
+          listen_port: listen_port
         )
 
       result_psql =
index 6db41fe6e36e9882f91649a98dff3a50e8440dbf..46552c7be67a25465dbdaab02a1f46013ce3b166 100644 (file)
@@ -344,5 +344,5 @@ defmodule Pleroma.Activity do
     )
   end
 
-  defdelegate search(user, query), to: Pleroma.Activity.Search
+  defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search
 end
index 0aa2aab23dded19cf9426ed6e8e3b2a7b51a3d01..0cc3770a70a6b13664242a34b0bd52c558321938 100644 (file)
@@ -5,14 +5,17 @@
 defmodule Pleroma.Activity.Search do
   alias Pleroma.Activity
   alias Pleroma.Object.Fetcher
-  alias Pleroma.Repo
+  alias Pleroma.Pagination
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.Visibility
 
   import Ecto.Query
 
-  def search(user, search_query) do
+  def search(user, search_query, options \\ []) do
     index_type = if Pleroma.Config.get([:database, :rum_enabled]), do: :rum, else: :gin
+    limit = Enum.min([Keyword.get(options, :limit), 40])
+    offset = Keyword.get(options, :offset, 0)
+    author = Keyword.get(options, :author)
 
     Activity
     |> Activity.with_preloaded_object()
@@ -20,15 +23,23 @@ defmodule Pleroma.Activity.Search do
     |> restrict_public()
     |> query_with(index_type, search_query)
     |> maybe_restrict_local(user)
-    |> Repo.all()
+    |> maybe_restrict_author(author)
+    |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => limit}, :offset)
     |> maybe_fetch(user, search_query)
   end
 
+  def maybe_restrict_author(query, %User{} = author) do
+    from([a, o] in query,
+      where: a.actor == ^author.ap_id
+    )
+  end
+
+  def maybe_restrict_author(query, _), do: query
+
   defp restrict_public(q) do
     from([a, o] in q,
       where: fragment("?->>'type' = 'Create'", a.data),
-      where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients,
-      limit: 40
+      where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients
     )
   end
 
index 8887e393536db2ef6a60bd8ee5c2ce979a40925a..29cd144770f5d4ca1d635ca328e541d9ca87c48a 100644 (file)
@@ -155,11 +155,7 @@ defmodule Pleroma.Application do
             start: {Pleroma.Web.Endpoint, :start_link, []},
             type: :supervisor
           },
-          %{id: Pleroma.Gopher.Server, start: {Pleroma.Gopher.Server, :start_link, []}},
-          %{
-            id: Pleroma.User.SynchronizationWorker,
-            start: {Pleroma.User.SynchronizationWorker, :start_link, []}
-          }
+          %{id: Pleroma.Gopher.Server, start: {Pleroma.Gopher.Server, :start_link, []}}
         ]
 
     # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
index a2c153720365778b311ea65d0a743605f176be99..79f133ea64948b37c258b290f874976462995f70 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.BBS.Authenticator do
   use Sshd.PasswordAuthenticator
   alias Comeonin.Pbkdf2
index f34be961f15065d37d86b6a7c83adc52349b1c4e..0a381f5920c954517b3dfdf1a31bf022cb0b4601 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.BBS.Handler do
   use Sshd.ShellHandler
   alias Pleroma.Activity
index 7f8fd43b6f174f2367022727f6292956396f1d9c..d976f949c4f549cae5be55bf43eb05470cab70f9 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Bookmark do
   use Ecto.Schema
 
index f105cbb2529bc97a1ad42c6f37de050ddc546044..a73b872517dc6b47c8386a79535bf8b8558c6852 100644 (file)
@@ -3,6 +3,8 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Captcha do
+  import Pleroma.Web.Gettext
+
   alias Calendar.DateTime
   alias Plug.Crypto.KeyGenerator
   alias Plug.Crypto.MessageEncryptor
@@ -83,10 +85,11 @@ defmodule Pleroma.Captcha do
       with {:ok, data} <- MessageEncryptor.decrypt(answer_data, secret, sign_secret),
            %{at: at, answer_data: answer_md5} <- :erlang.binary_to_term(data) do
         try do
-          if DateTime.before?(at, valid_if_after), do: throw({:error, "CAPTCHA expired"})
+          if DateTime.before?(at, valid_if_after),
+            do: throw({:error, dgettext("errors", "CAPTCHA expired")})
 
           if not is_nil(Cachex.get!(:used_captcha_cache, token)),
-            do: throw({:error, "CAPTCHA already used"})
+            do: throw({:error, dgettext("errors", "CAPTCHA already used")})
 
           res = method().validate(token, captcha, answer_md5)
           # Throw if an error occurs
@@ -101,7 +104,7 @@ defmodule Pleroma.Captcha do
           :throw, e -> e
         end
       else
-        _ -> {:error, "Invalid answer data"}
+        _ -> {:error, dgettext("errors", "Invalid answer data")}
       end
 
     {:reply, result, state}
index 18931d5a07b19193822c8a941259564397e0aecf..4e1a07c596bcee16e4bf405aed785f178a2791de 100644 (file)
@@ -3,6 +3,7 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Captcha.Kocaptcha do
+  import Pleroma.Web.Gettext
   alias Pleroma.Captcha.Service
   @behaviour Service
 
@@ -12,7 +13,7 @@ defmodule Pleroma.Captcha.Kocaptcha do
 
     case Tesla.get(endpoint <> "/new") do
       {:error, _} ->
-        %{error: "Kocaptcha service unavailable"}
+        %{error: dgettext("errors", "Kocaptcha service unavailable")}
 
       {:ok, res} ->
         json_resp = Jason.decode!(res.body)
@@ -32,6 +33,6 @@ defmodule Pleroma.Captcha.Kocaptcha do
     if not is_nil(captcha) and
          :crypto.hash(:md5, captcha) |> Base.encode16() == String.upcase(answer_data),
        do: :ok,
-       else: {:error, "Invalid CAPTCHA"}
+       else: {:error, dgettext("errors", "Invalid CAPTCHA")}
   end
 end
index cf880aa22939efeacd7182a52d2c74d13889d862..3c13a055800d51479b53470140f36c0a9cac2aa6 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Config.TransferTask do
   use Task
   alias Pleroma.Web.AdminAPI.Config
index 53f5a661c441eca67ec63c1feed66edf9a4c062e..2e4657b7c33b4a1ce9762f5a0e6facc80dfe9e53 100644 (file)
@@ -3,11 +3,58 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Emails.Mailer do
-  use Swoosh.Mailer, otp_app: :pleroma
+  @moduledoc """
+  Defines the Pleroma mailer.
 
+  The module contains functions to delivery email using Swoosh.Mailer.
+  """
+
+  alias Swoosh.DeliveryError
+
+  @otp_app :pleroma
+  @mailer_config [otp: :pleroma]
+
+  @spec enabled?() :: boolean()
+  def enabled?, do: Pleroma.Config.get([__MODULE__, :enabled])
+
+  @doc "add email to queue"
   def deliver_async(email, config \\ []) do
     PleromaJobQueue.enqueue(:mailer, __MODULE__, [:deliver_async, email, config])
   end
 
+  @doc "callback to perform send email from queue"
   def perform(:deliver_async, email, config), do: deliver(email, config)
+
+  @spec deliver(Swoosh.Email.t(), Keyword.t()) :: {:ok, term} | {:error, term}
+  def deliver(email, config \\ [])
+
+  def deliver(email, config) do
+    case enabled?() do
+      true -> Swoosh.Mailer.deliver(email, parse_config(config))
+      false -> {:error, :deliveries_disabled}
+    end
+  end
+
+  @spec deliver!(Swoosh.Email.t(), Keyword.t()) :: term | no_return
+  def deliver!(email, config \\ [])
+
+  def deliver!(email, config) do
+    case deliver(email, config) do
+      {:ok, result} -> result
+      {:error, reason} -> raise DeliveryError, reason: reason
+    end
+  end
+
+  @on_load :validate_dependency
+
+  @doc false
+  def validate_dependency do
+    parse_config([])
+    |> Keyword.get(:adapter)
+    |> Swoosh.Mailer.validate_dependency()
+  end
+
+  defp parse_config(config) do
+    Swoosh.Mailer.parse_config(@otp_app, __MODULE__, @mailer_config, config)
+  end
 end
index fa5043bc595c472ef8ddcfd1f5d5217704b122c2..1b05d573c053beacb4b9d3c0e476338a00e39d23 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Instances do
   @moduledoc "Instances context."
 
index 420803a8fa84203c28335d0e5c198a737d0c1d20..4d7ed4ca1076abe35b697f2e29002f739b2124c8 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Instances.Instance do
   @moduledoc "Instance."
 
index b8647dd26ed9f8aa9bc9a2f661e8eee34a28525c..c8d339c190ba048bc2248f29dfac1807c634cbd9 100644 (file)
@@ -44,7 +44,15 @@ defmodule Pleroma.Object do
     Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id)))
   end
 
+  defp warn_on_no_object_preloaded(ap_id) do
+    "Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object"
+    |> Logger.debug()
+
+    Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
+  end
+
   def normalize(_, fetch_remote \\ true, options \\ [])
+
   # If we pass an Activity to Object.normalize(), we can try to use the preloaded object.
   # Use this whenever possible, especially when walking graphs in an O(N) loop!
   def normalize(%Object{} = object, _, _), do: object
@@ -55,25 +63,15 @@ defmodule Pleroma.Object do
     %Object{id: "pleroma:fake_object_id", data: data}
   end
 
-  # Catch and log Object.normalize() calls where the Activity's child object is not
-  # preloaded.
+  # No preloaded object
   def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}, fetch_remote, _) do
-    Logger.debug(
-      "Object.normalize() called without preloaded object (#{ap_id}).  Consider preloading the object!"
-    )
-
-    Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
-
+    warn_on_no_object_preloaded(ap_id)
     normalize(ap_id, fetch_remote)
   end
 
+  # No preloaded object
   def normalize(%Activity{data: %{"object" => ap_id}}, fetch_remote, _) do
-    Logger.debug(
-      "Object.normalize() called without preloaded object (#{ap_id}).  Consider preloading the object!"
-    )
-
-    Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
-
+    warn_on_no_object_preloaded(ap_id)
     normalize(ap_id, fetch_remote)
   end
 
index fffbf2bbb23062bdb983e922d95bf0e6f1aed069..101c21f9629e8da4b33b4d988baedeafe8d3b608 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Object.Fetcher do
   alias Pleroma.HTTP
   alias Pleroma.Object
index 64d836d3efd7e86120c0450975eee1dba98e7a63..fe947ffd30f13b923ed220c6c633ce950f3d5779 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.ObjectTombstone do
   @enforce_keys [:id, :formerType, :deleted]
   defstruct [:id, :formerType, :deleted, type: "Tombstone"]
index f435e5c9cb43c0d262ef231d3a9985d2eab75a9b..2b869ccdcc494c2a9bb158772da59c1167aa7529 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Pagination do
   @moduledoc """
   Implements Mastodon-compatible pagination.
@@ -10,16 +14,28 @@ defmodule Pleroma.Pagination do
 
   @default_limit 20
 
-  def fetch_paginated(query, params) do
+  def fetch_paginated(query, params, type \\ :keyset)
+
+  def fetch_paginated(query, params, :keyset) do
     options = cast_params(params)
 
     query
-    |> paginate(options)
+    |> paginate(options, :keyset)
     |> Repo.all()
     |> enforce_order(options)
   end
 
-  def paginate(query, options) do
+  def fetch_paginated(query, params, :offset) do
+    options = cast_params(params)
+
+    query
+    |> paginate(options, :offset)
+    |> Repo.all()
+  end
+
+  def paginate(query, options, method \\ :keyset)
+
+  def paginate(query, options, :keyset) do
     query
     |> restrict(:min_id, options)
     |> restrict(:since_id, options)
@@ -28,11 +44,18 @@ defmodule Pleroma.Pagination do
     |> restrict(:limit, options)
   end
 
+  def paginate(query, options, :offset) do
+    query
+    |> restrict(:offset, options)
+    |> restrict(:limit, options)
+  end
+
   defp cast_params(params) do
     param_types = %{
       min_id: :string,
       since_id: :string,
       max_id: :string,
+      offset: :integer,
       limit: :integer
     }
 
@@ -66,6 +89,10 @@ defmodule Pleroma.Pagination do
     order_by(query, [u], fragment("? desc nulls last", u.id))
   end
 
+  defp restrict(query, :offset, %{offset: offset}) do
+    offset(query, ^offset)
+  end
+
   defp restrict(query, :limit, options) do
     limit = Map.get(options, :limit, @default_limit)
 
index 11c4342c40e6fb74d16c726c98ad36d9feff1a0e..27cd41aec327a38146d8a7ded5be5049f02fa1d9 100644 (file)
@@ -4,6 +4,7 @@
 
 defmodule Pleroma.Plugs.EnsureAuthenticatedPlug do
   import Plug.Conn
+  import Pleroma.Web.TranslationHelpers
   alias Pleroma.User
 
   def init(options) do
@@ -16,8 +17,7 @@ defmodule Pleroma.Plugs.EnsureAuthenticatedPlug do
 
   def call(conn, _) do
     conn
-    |> put_resp_content_type("application/json")
-    |> send_resp(403, Jason.encode!(%{error: "Invalid credentials."}))
+    |> render_error(:forbidden, "Invalid credentials.")
     |> halt
   end
 end
index 317fd5445008a89eda6778ccd08cdf936d1320fa..a16f6143560cab615ee016fab33a2e13f189db52 100644 (file)
@@ -3,6 +3,7 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug do
+  import Pleroma.Web.TranslationHelpers
   import Plug.Conn
   alias Pleroma.Config
   alias Pleroma.User
@@ -23,8 +24,7 @@ defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug do
 
       {false, _} ->
         conn
-        |> put_resp_content_type("application/json")
-        |> send_resp(403, Jason.encode!(%{error: "This resource requires authentication."}))
+        |> render_error(:forbidden, "This resource requires authentication.")
         |> halt
     end
   end
index f2bfa2b1a7c9e3699fb6c97d841783ffce5b0d81..b508628a92dca12b90e197c1acf3a3ca3c66b4bc 100644 (file)
@@ -4,6 +4,7 @@
 
 defmodule Pleroma.Plugs.OAuthScopesPlug do
   import Plug.Conn
+  import Pleroma.Web.Gettext
 
   @behaviour Plug
 
@@ -30,11 +31,14 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
 
       true ->
         missing_scopes = scopes -- token.scopes
-        error_message = "Insufficient permissions: #{Enum.join(missing_scopes, " #{op} ")}."
+        permissions = Enum.join(missing_scopes, " #{op} ")
+
+        error_message =
+          dgettext("errors", "Insufficient permissions: %{permissions}.", permissions: permissions)
 
         conn
         |> put_resp_content_type("application/json")
-        |> send_resp(403, Jason.encode!(%{error: error_message}))
+        |> send_resp(:forbidden, Jason.encode!(%{error: error_message}))
         |> halt()
     end
   end
index 9ba5875fa31aa21434f6a129d99da2e9b1b64634..c5e0957e8823b2369ed414c26f2a57348b26821d 100644 (file)
@@ -44,8 +44,7 @@ defmodule Pleroma.Plugs.RateLimiter do
         ...
       end
   """
-
-  import Phoenix.Controller, only: [json: 2]
+  import Pleroma.Web.TranslationHelpers
   import Plug.Conn
 
   alias Pleroma.User
@@ -63,7 +62,7 @@ defmodule Pleroma.Plugs.RateLimiter do
   def call(conn, opts) do
     case check_rate(conn, opts) do
       {:ok, _count} -> conn
-      {:error, _count} -> render_error(conn)
+      {:error, _count} -> render_throttled_error(conn)
     end
   end
 
@@ -85,10 +84,9 @@ defmodule Pleroma.Plugs.RateLimiter do
     |> Enum.join(".")
   end
 
-  defp render_error(conn) do
+  defp render_throttled_error(conn) do
     conn
-    |> put_status(:too_many_requests)
-    |> json(%{error: "Throttled"})
+    |> render_error(:too_many_requests, "Throttled")
     |> halt()
   end
 end
diff --git a/lib/pleroma/plugs/set_locale_plug.ex b/lib/pleroma/plugs/set_locale_plug.ex
new file mode 100644 (file)
index 0000000..8646cb3
--- /dev/null
@@ -0,0 +1,63 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+# NOTE: this module is based on https://github.com/smeevil/set_locale
+defmodule Pleroma.Plugs.SetLocalePlug do
+  import Plug.Conn, only: [get_req_header: 2, assign: 3]
+
+  def init(_), do: nil
+
+  def call(conn, _) do
+    locale = get_locale_from_header(conn) || Gettext.get_locale()
+    Gettext.put_locale(locale)
+    assign(conn, :locale, locale)
+  end
+
+  defp get_locale_from_header(conn) do
+    conn
+    |> extract_accept_language()
+    |> Enum.find(&supported_locale?/1)
+  end
+
+  defp extract_accept_language(conn) do
+    case get_req_header(conn, "accept-language") do
+      [value | _] ->
+        value
+        |> String.split(",")
+        |> Enum.map(&parse_language_option/1)
+        |> Enum.sort(&(&1.quality > &2.quality))
+        |> Enum.map(& &1.tag)
+        |> Enum.reject(&is_nil/1)
+        |> ensure_language_fallbacks()
+
+      _ ->
+        []
+    end
+  end
+
+  defp supported_locale?(locale) do
+    Pleroma.Web.Gettext
+    |> Gettext.known_locales()
+    |> Enum.member?(locale)
+  end
+
+  defp parse_language_option(string) do
+    captures = Regex.named_captures(~r/^\s?(?<tag>[\w\-]+)(?:;q=(?<quality>[\d\.]+))?$/i, string)
+
+    quality =
+      case Float.parse(captures["quality"] || "1.0") do
+        {val, _} -> val
+        :error -> 1.0
+      end
+
+    %{tag: captures["tag"], quality: quality}
+  end
+
+  defp ensure_language_fallbacks(tags) do
+    Enum.flat_map(tags, fn tag ->
+      [language | _] = String.split(tag, "-")
+      if Enum.member?(tags, language), do: [tag], else: [tag, language]
+    end)
+  end
+end
index 8d0fac7eed37e5ff78e021567dd1a4152390ec3b..69c1ab942769ca657a076fe5072d6163c56dcbd8 100644 (file)
@@ -7,6 +7,7 @@ defmodule Pleroma.Plugs.UploadedMedia do
   """
 
   import Plug.Conn
+  import Pleroma.Web.Gettext
   require Logger
 
   @behaviour Plug
@@ -45,7 +46,7 @@ defmodule Pleroma.Plugs.UploadedMedia do
     else
       _ ->
         conn
-        |> send_resp(500, "Failed")
+        |> send_resp(:internal_server_error, dgettext("errors", "Failed"))
         |> halt()
     end
   end
@@ -64,7 +65,7 @@ defmodule Pleroma.Plugs.UploadedMedia do
       conn
     else
       conn
-      |> send_resp(404, "Not found")
+      |> send_resp(:not_found, dgettext("errors", "Not found"))
       |> halt()
     end
   end
@@ -84,7 +85,7 @@ defmodule Pleroma.Plugs.UploadedMedia do
     Logger.error("#{__MODULE__}: Unknown get startegy: #{inspect(unknown)}")
 
     conn
-    |> send_resp(500, "Internal Error")
+    |> send_resp(:internal_server_error, dgettext("errors", "Internal Error"))
     |> halt()
   end
 end
index 04329e919fceb88bfe9c22bec8c70b6639e36636..4c4b3d610c15ec69e07cd7d2ea63b46d1bc4df68 100644 (file)
@@ -3,6 +3,7 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Plugs.UserIsAdminPlug do
+  import Pleroma.Web.TranslationHelpers
   import Plug.Conn
   alias Pleroma.User
 
@@ -16,8 +17,7 @@ defmodule Pleroma.Plugs.UserIsAdminPlug do
 
   def call(conn, _) do
     conn
-    |> put_resp_content_type("application/json")
-    |> send_resp(403, Jason.encode!(%{error: "User is not admin."}))
+    |> render_error(:forbidden, "User is not admin.")
     |> halt
   end
 end
index 57c2d2cfdc4a50288b142dcd7b8ebcafec7fb64a..776c4794c013811a26488bbbaa386371a6250ee4 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.ReverseProxy.Client do
   @callback request(atom(), String.t(), [tuple()], String.t(), list()) ::
               {:ok, pos_integer(), [tuple()], reference() | map()}
index bf15389fcde3c46ed9ecf9f8a1c5f3ebcd514813..0af76bc593f4e69d245678686885cf7a5f32268e 100644 (file)
@@ -3,6 +3,8 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Uploaders.Uploader do
+  import Pleroma.Web.Gettext
+
   @moduledoc """
   Defines the contract to put and get an uploaded file to any backend.
   """
@@ -66,7 +68,7 @@ defmodule Pleroma.Uploaders.Uploader do
             {:error, error}
         end
     after
-      30_000 -> {:error, "Uploader callback timeout"}
+      30_000 -> {:error, dgettext("errors", "Uploader callback timeout")}
     end
   end
 end
index 791676ee28e5e9327714d4ef2304e04068fb5e32..9eb13084f342f61b84c3a5216a2c05a4b9084b9d 100644 (file)
@@ -52,6 +52,7 @@ defmodule Pleroma.User do
     field(:avatar, :map)
     field(:local, :boolean, default: true)
     field(:follower_address, :string)
+    field(:following_address, :string)
     field(:search_rank, :float, virtual: true)
     field(:search_type, :integer, virtual: true)
     field(:tags, {:array, :string}, default: [])
@@ -108,6 +109,10 @@ defmodule Pleroma.User do
   def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
   def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers"
 
+  @spec ap_following(User.t()) :: Sring.t()
+  def ap_following(%User{following_address: fa}) when is_binary(fa), do: fa
+  def ap_following(%User{} = user), do: "#{ap_id(user)}/following"
+
   def user_info(%User{} = user, args \\ %{}) do
     following_count =
       if args[:following_count], do: args[:following_count], else: following_count(user)
@@ -129,6 +134,7 @@ defmodule Pleroma.User do
     Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user, args))
   end
 
+  @spec restrict_deactivated(Ecto.Query.t()) :: Ecto.Query.t()
   def restrict_deactivated(query) do
     from(u in query,
       where: not fragment("? \\? 'deactivated' AND ?->'deactivated' @> 'true'", u.info, u.info)
@@ -163,9 +169,10 @@ defmodule Pleroma.User do
 
     if changes.valid? do
       case info_cng.changes[:source_data] do
-        %{"followers" => followers} ->
+        %{"followers" => followers, "following" => following} ->
           changes
           |> put_change(:follower_address, followers)
+          |> put_change(:following_address, following)
 
         _ ->
           followers = User.ap_followers(%User{nickname: changes.changes[:nickname]})
@@ -197,7 +204,14 @@ defmodule Pleroma.User do
       |> User.Info.user_upgrade(params[:info])
 
     struct
-    |> cast(params, [:bio, :name, :follower_address, :avatar, :last_refreshed_at])
+    |> cast(params, [
+      :bio,
+      :name,
+      :follower_address,
+      :following_address,
+      :avatar,
+      :last_refreshed_at
+    ])
     |> unique_constraint(:nickname)
     |> validate_format(:nickname, local_nickname_regex())
     |> validate_length(:bio, max: 5000)
@@ -938,6 +952,8 @@ defmodule Pleroma.User do
 
   @spec perform(atom(), User.t()) :: {:ok, User.t()}
   def perform(:delete, %User{} = user) do
+    {:ok, _user} = ActivityPub.delete(user)
+
     # Remove all relationships
     {:ok, followers} = User.get_followers(user)
 
@@ -954,8 +970,8 @@ defmodule Pleroma.User do
     end)
 
     delete_user_activities(user)
-
-    {:ok, _user} = Repo.delete(user)
+    invalidate_cache(user)
+    Repo.delete(user)
   end
 
   @spec perform(atom(), User.t()) :: {:ok, User.t()}
@@ -1011,42 +1027,20 @@ defmodule Pleroma.User do
     )
   end
 
-  @spec sync_follow_counter() :: :ok
-  def sync_follow_counter,
-    do: PleromaJobQueue.enqueue(:background, __MODULE__, [:sync_follow_counters])
-
-  @spec perform(:sync_follow_counters) :: :ok
-  def perform(:sync_follow_counters) do
-    {:ok, _pid} = Agent.start_link(fn -> %{} end, name: :domain_errors)
-    config = Pleroma.Config.get([:instance, :external_user_synchronization])
-
-    :ok = sync_follow_counters(config)
-    Agent.stop(:domain_errors)
-  end
-
-  @spec sync_follow_counters(keyword()) :: :ok
-  def sync_follow_counters(opts \\ []) do
-    users = external_users(opts)
-
-    if length(users) > 0 do
-      errors = Agent.get(:domain_errors, fn state -> state end)
-      {last, updated_errors} = User.Synchronization.call(users, errors, opts)
-      Agent.update(:domain_errors, fn _state -> updated_errors end)
-      sync_follow_counters(max_id: last.id, limit: opts[:limit])
-    else
-      :ok
-    end
+  @spec external_users_query() :: Ecto.Query.t()
+  def external_users_query do
+    User.Query.build(%{
+      external: true,
+      active: true,
+      order_by: :id
+    })
   end
 
   @spec external_users(keyword()) :: [User.t()]
   def external_users(opts \\ []) do
     query =
-      User.Query.build(%{
-        external: true,
-        active: true,
-        order_by: :id,
-        select: [:id, :ap_id, :info]
-      })
+      external_users_query()
+      |> select([u], struct(u, [:id, :ap_id, :info]))
 
     query =
       if opts[:max_id],
index 64eb6d2bc484df842a22562b7468d80d807aba86..46620b89acf42517429aa6a1e1cb1f77d0d95c73 100644 (file)
@@ -3,6 +3,7 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.User.Search do
+  alias Pleroma.Pagination
   alias Pleroma.Repo
   alias Pleroma.User
   import Ecto.Query
@@ -18,8 +19,7 @@ defmodule Pleroma.User.Search do
 
     for_user = Keyword.get(opts, :for_user)
 
-    # Strip the beginning @ off if there is a query
-    query_string = String.trim_leading(query_string, "@")
+    query_string = format_query(query_string)
 
     maybe_resolve(resolve, for_user, query_string)
 
@@ -33,13 +33,24 @@ defmodule Pleroma.User.Search do
 
         query_string
         |> search_query(for_user, following)
-        |> paginate(result_limit, offset)
-        |> Repo.all()
+        |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset)
       end)
 
     results
   end
 
+  defp format_query(query_string) do
+    # Strip the beginning @ off if there is a query
+    query_string = String.trim_leading(query_string, "@")
+
+    with [name, domain] <- String.split(query_string, "@"),
+         formatted_domain <- String.replace(domain, ~r/[!-\-|@|[-`|{-~|\/|:]+/, "") do
+      name <> "@" <> to_string(:idna.encode(formatted_domain))
+    else
+      _ -> query_string
+    end
+  end
+
   defp search_query(query_string, for_user, following) do
     for_user
     |> base_query(following)
@@ -76,10 +87,6 @@ defmodule Pleroma.User.Search do
 
   defp filter_blocked_domains(query, _), do: query
 
-  defp paginate(query, limit, offset) do
-    from(q in query, limit: ^limit, offset: ^offset)
-  end
-
   defp union_subqueries({fts_subquery, trigram_subquery}) do
     from(s in trigram_subquery, union_all: ^fts_subquery)
   end
@@ -151,7 +158,7 @@ defmodule Pleroma.User.Search do
   defp fts_search_subquery(query, term) do
     processed_query =
       String.trim_trailing(term, "@" <> local_domain())
-      |> String.replace(~r/\W+/, " ")
+      |> String.replace(~r/[!-\/|@|[-`|{-~|:-?]+/, " ")
       |> String.trim()
       |> String.split()
       |> Enum.map(&(&1 <> ":*"))
diff --git a/lib/pleroma/user/synchronization.ex b/lib/pleroma/user/synchronization.ex
deleted file mode 100644 (file)
index 93660e0..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.User.Synchronization do
-  alias Pleroma.HTTP
-  alias Pleroma.User
-
-  @spec call([User.t()], map(), keyword()) :: {User.t(), map()}
-  def call(users, errors, opts \\ []) do
-    do_call(users, errors, opts)
-  end
-
-  defp do_call([user | []], errors, opts) do
-    updated = fetch_counters(user, errors, opts)
-    {user, updated}
-  end
-
-  defp do_call([user | others], errors, opts) do
-    updated = fetch_counters(user, errors, opts)
-    do_call(others, updated, opts)
-  end
-
-  defp fetch_counters(user, errors, opts) do
-    %{host: host} = URI.parse(user.ap_id)
-
-    info = %{}
-    {following, errors} = fetch_counter(user.ap_id <> "/following", host, errors, opts)
-    info = if following, do: Map.put(info, :following_count, following), else: info
-
-    {followers, errors} = fetch_counter(user.ap_id <> "/followers", host, errors, opts)
-    info = if followers, do: Map.put(info, :follower_count, followers), else: info
-
-    User.set_info_cache(user, info)
-    errors
-  end
-
-  defp available_domain?(domain, errors, opts) do
-    max_retries = Keyword.get(opts, :max_retries, 3)
-    not (Map.has_key?(errors, domain) && errors[domain] >= max_retries)
-  end
-
-  defp fetch_counter(url, host, errors, opts) do
-    with true <- available_domain?(host, errors, opts),
-         {:ok, %{body: body, status: code}} when code in 200..299 <-
-           HTTP.get(
-             url,
-             [{:Accept, "application/activity+json"}]
-           ),
-         {:ok, data} <- Jason.decode(body) do
-      {data["totalItems"], errors}
-    else
-      false ->
-        {nil, errors}
-
-      _ ->
-        {nil, Map.update(errors, host, 1, &(&1 + 1))}
-    end
-  end
-end
diff --git a/lib/pleroma/user/synchronization_worker.ex b/lib/pleroma/user/synchronization_worker.ex
deleted file mode 100644 (file)
index ba9cc35..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-onl
-
-defmodule Pleroma.User.SynchronizationWorker do
-  use GenServer
-
-  def start_link do
-    config = Pleroma.Config.get([:instance, :external_user_synchronization])
-
-    if config[:enabled] do
-      GenServer.start_link(__MODULE__, interval: config[:interval])
-    else
-      :ignore
-    end
-  end
-
-  def init(opts) do
-    schedule_next(opts)
-    {:ok, opts}
-  end
-
-  def handle_info(:sync_follow_counters, opts) do
-    Pleroma.User.sync_follow_counter()
-    schedule_next(opts)
-    {:noreply, opts}
-  end
-
-  defp schedule_next(opts) do
-    Process.send_after(self(), :sync_follow_counters, opts[:interval])
-  end
-end
index 2ba65b75acad271254baa5bc37e2e6061f9d7548..99fba729e272fe7b17adced17eebac2f868648c2 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.User.WelcomeMessage do
   alias Pleroma.User
   alias Pleroma.Web.CommonAPI
index 55315d66ed454e320615f153e8f884d132a00502..a3174a7871fc2f4d8188b26125ccf9ee8a832330 100644 (file)
@@ -405,6 +405,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     end
   end
 
+  def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
+    with data <- %{
+           "to" => [follower_address],
+           "type" => "Delete",
+           "actor" => ap_id,
+           "object" => %{"type" => "Person", "id" => ap_id}
+         },
+         {:ok, activity} <- insert(data, true, true),
+         :ok <- maybe_federate(activity) do
+      {:ok, user}
+    end
+  end
+
   def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
     user = User.get_cached_by_ap_id(actor)
     to = (object.data["to"] || []) ++ (object.data["cc"] || [])
@@ -981,6 +994,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
       avatar: avatar,
       name: data["name"],
       follower_address: data["followers"],
+      following_address: data["following"],
       bio: data["summary"]
     }
 
index 0182bda46d714462482926b87fb2b1c0ec707e88..cf517620182e039a82bfedb32721ddd5e47972c9 100644 (file)
@@ -31,9 +31,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
       conn
     else
       conn
-      |> put_status(404)
-      |> json(%{error: "not found"})
-      |> halt
+      |> render_error(:not_found, "not found")
+      |> halt()
     end
   end
 
@@ -190,7 +189,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
       Logger.info(inspect(conn.req_headers))
     end
 
-    json(conn, "error")
+    json(conn, dgettext("errors", "error"))
   end
 
   def relay(conn, _params) do
@@ -218,9 +217,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
       |> put_resp_header("content-type", "application/activity+json")
       |> json(UserView.render("inbox.json", %{user: user, max_id: params["max_id"]}))
     else
+      err =
+        dgettext("errors", "can't read inbox of %{nickname} as %{as_nickname}",
+          nickname: nickname,
+          as_nickname: user.nickname
+        )
+
       conn
       |> put_status(:forbidden)
-      |> json("can't read inbox of #{nickname} as #{user.nickname}")
+      |> json(err)
     end
   end
 
@@ -246,7 +251,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
          {:ok, delete} <- ActivityPub.delete(object) do
       {:ok, delete}
     else
-      _ -> {:error, "Can't delete object"}
+      _ -> {:error, dgettext("errors", "Can't delete object")}
     end
   end
 
@@ -255,12 +260,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
          {:ok, activity, _object} <- ActivityPub.like(user, object) do
       {:ok, activity}
     else
-      _ -> {:error, "Can't like object"}
+      _ -> {:error, dgettext("errors", "Can't like object")}
     end
   end
 
   def handle_user_activity(_, _) do
-    {:error, "Unhandled activity type"}
+    {:error, dgettext("errors", "Unhandled activity type")}
   end
 
   def update_outbox(
@@ -288,22 +293,28 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
           |> json(message)
       end
     else
+      err =
+        dgettext("errors", "can't update outbox of %{nickname} as %{as_nickname}",
+          nickname: nickname,
+          as_nickname: user.nickname
+        )
+
       conn
       |> put_status(:forbidden)
-      |> json("can't update outbox of #{nickname} as #{user.nickname}")
+      |> json(err)
     end
   end
 
   def errors(conn, {:error, :not_found}) do
     conn
-    |> put_status(404)
-    |> json("Not found")
+    |> put_status(:not_found)
+    |> json(dgettext("errors", "Not found"))
   end
 
   def errors(conn, _e) do
     conn
-    |> put_status(500)
-    |> json("error")
+    |> put_status(:internal_server_error)
+    |> json(dgettext("errors", "error"))
   end
 
   defp set_requester_reachable(%Plug.Conn{} = conn, _) do
index 15d8514be577f1117cae0e868d523b3412d79182..2d03df68afefd9943576d9ec5713d72015b2fd3a 100644 (file)
@@ -9,8 +9,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
   @behaviour Pleroma.Web.ActivityPub.MRF
 
   @reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless])
+
   def filter_by_summary(
-        %{"summary" => parent_summary} = _parent,
+        %{data: %{"summary" => parent_summary}} = _in_reply_to,
         %{"summary" => child_summary} = child
       )
       when not is_nil(child_summary) and byte_size(child_summary) > 0 and
@@ -24,17 +25,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
     end
   end
 
-  def filter_by_summary(_parent, child), do: child
-
-  def filter(%{"type" => activity_type} = object) when activity_type == "Create" do
-    child = object["object"]
-    in_reply_to = Object.normalize(child["inReplyTo"])
+  def filter_by_summary(_in_reply_to, child), do: child
 
+  def filter(%{"type" => "Create", "object" => child_object} = object) do
     child =
-      if(in_reply_to,
-        do: filter_by_summary(in_reply_to.data, child),
-        else: child
-      )
+      child_object["inReplyTo"]
+      |> Object.normalize(child_object["inReplyTo"])
+      |> filter_by_summary(child_object)
 
     object = Map.put(object, "object", child)
 
index f30fee0d506068cfdaa588c2c03515cdcb542ad1..86a48bda58dc4a0c9e2e2b2f63a2142fc587d7b1 100644 (file)
@@ -10,19 +10,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do
   def filter(
         %{
           "type" => "Create",
-          "object" => %{"content" => content, "attachment" => _attachment} = child_object
+          "object" => %{"content" => content, "attachment" => _} = _child_object
         } = object
       )
       when content in [".", "<p>.</p>"] do
-    child_object =
-      child_object
-      |> Map.put("content", "")
-
-    object =
-      object
-      |> Map.put("object", child_object)
-
-    {:ok, object}
+    {:ok, put_in(object, ["object", "content"], "")}
   end
 
   @impl true
index 9c87c69638b080149fa4eb76150e7a157df8ce2d..c269d0f892a0f399c7a820c4d2781bc71bc327ae 100644 (file)
@@ -8,18 +8,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
 
   @behaviour Pleroma.Web.ActivityPub.MRF
 
-  def filter(%{"type" => activity_type} = object) when activity_type == "Create" do
+  def filter(%{"type" => "Create", "object" => child_object} = object) do
     scrub_policy = Pleroma.Config.get([:mrf_normalize_markup, :scrub_policy])
 
-    child = object["object"]
-
     content =
-      child["content"]
+      child_object["content"]
       |> HTML.filter_tags(scrub_policy)
 
-    child = Map.put(child, "content", content)
-
-    object = Map.put(object, "object", child)
+    object = put_in(object, ["object", "content"], content)
 
     {:ok, object}
   end
index ea3df1b4d0eb98a894f35144c64597bbac15930a..da13fd7c7b9308030f690b4f364fa7b8891340af 100644 (file)
@@ -3,46 +3,42 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
-  alias Pleroma.User
   @moduledoc "Rejects non-public (followers-only, direct) activities"
+
+  alias Pleroma.Config
+  alias Pleroma.User
+
   @behaviour Pleroma.Web.ActivityPub.MRF
 
+  @public "https://www.w3.org/ns/activitystreams#Public"
+
   @impl true
   def filter(%{"type" => "Create"} = object) do
     user = User.get_cached_by_ap_id(object["actor"])
-    public = "https://www.w3.org/ns/activitystreams#Public"
 
     # Determine visibility
     visibility =
       cond do
-        public in object["to"] -> "public"
-        public in object["cc"] -> "unlisted"
+        @public in object["to"] -> "public"
+        @public in object["cc"] -> "unlisted"
         user.follower_address in object["to"] -> "followers"
         true -> "direct"
       end
 
-    policy = Pleroma.Config.get(:mrf_rejectnonpublic)
+    policy = Config.get(:mrf_rejectnonpublic)
+
+    cond do
+      visibility in ["public", "unlisted"] ->
+        {:ok, object}
 
-    case visibility do
-      "public" ->
+      visibility == "followers" and Keyword.get(policy, :allow_followersonly) ->
         {:ok, object}
 
-      "unlisted" ->
+      visibility == "direct" and Keyword.get(policy, :allow_direct) ->
         {:ok, object}
 
-      "followers" ->
-        with true <- Keyword.get(policy, :allow_followersonly) do
-          {:ok, object}
-        else
-          _e -> {:reject, nil}
-        end
-
-      "direct" ->
-        with true <- Keyword.get(policy, :allow_direct) do
-          {:ok, object}
-        else
-          _e -> {:reject, nil}
-        end
+      true ->
+        {:reject, nil}
     end
   end
 
index 6683b8d8e6a294b6ab19797f5f4d495fdfd08e1a..b42c4ed76b01612335f1744d0da6740cff6d12c3 100644 (file)
@@ -19,12 +19,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
      - `mrf_tag:disable-any-subscription`: Reject any follow requests
   """
 
+  @public "https://www.w3.org/ns/activitystreams#Public"
+
   defp get_tags(%User{tags: tags}) when is_list(tags), do: tags
   defp get_tags(_), do: []
 
   defp process_tag(
          "mrf_tag:media-force-nsfw",
-         %{"type" => "Create", "object" => %{"attachment" => child_attachment} = object} = message
+         %{
+           "type" => "Create",
+           "object" => %{"attachment" => child_attachment} = object
+         } = message
        )
        when length(child_attachment) > 0 do
     tags = (object["tag"] || []) ++ ["nsfw"]
@@ -41,7 +46,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
 
   defp process_tag(
          "mrf_tag:media-strip",
-         %{"type" => "Create", "object" => %{"attachment" => child_attachment} = object} = message
+         %{
+           "type" => "Create",
+           "object" => %{"attachment" => child_attachment} = object
+         } = message
        )
        when length(child_attachment) > 0 do
     object = Map.delete(object, "attachment")
@@ -52,19 +60,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
 
   defp process_tag(
          "mrf_tag:force-unlisted",
-         %{"type" => "Create", "to" => to, "cc" => cc, "actor" => actor} = message
+         %{
+           "type" => "Create",
+           "to" => to,
+           "cc" => cc,
+           "actor" => actor,
+           "object" => object
+         } = message
        ) do
     user = User.get_cached_by_ap_id(actor)
 
-    if Enum.member?(to, "https://www.w3.org/ns/activitystreams#Public") do
-      to =
-        List.delete(to, "https://www.w3.org/ns/activitystreams#Public") ++ [user.follower_address]
-
-      cc =
-        List.delete(cc, user.follower_address) ++ ["https://www.w3.org/ns/activitystreams#Public"]
+    if Enum.member?(to, @public) do
+      to = List.delete(to, @public) ++ [user.follower_address]
+      cc = List.delete(cc, user.follower_address) ++ [@public]
 
       object =
-        message["object"]
+        object
         |> Map.put("to", to)
         |> Map.put("cc", cc)
 
@@ -82,19 +93,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
 
   defp process_tag(
          "mrf_tag:sandbox",
-         %{"type" => "Create", "to" => to, "cc" => cc, "actor" => actor} = message
+         %{
+           "type" => "Create",
+           "to" => to,
+           "cc" => cc,
+           "actor" => actor,
+           "object" => object
+         } = message
        ) do
     user = User.get_cached_by_ap_id(actor)
 
-    if Enum.member?(to, "https://www.w3.org/ns/activitystreams#Public") or
-         Enum.member?(cc, "https://www.w3.org/ns/activitystreams#Public") do
-      to =
-        List.delete(to, "https://www.w3.org/ns/activitystreams#Public") ++ [user.follower_address]
-
-      cc = List.delete(cc, "https://www.w3.org/ns/activitystreams#Public")
+    if Enum.member?(to, @public) or Enum.member?(cc, @public) do
+      to = List.delete(to, @public) ++ [user.follower_address]
+      cc = List.delete(cc, @public)
 
       object =
-        message["object"]
+        object
         |> Map.put("to", to)
         |> Map.put("cc", cc)
 
@@ -123,7 +137,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
     end
   end
 
-  defp process_tag("mrf_tag:disable-any-subscription", %{"type" => "Follow"}), do: {:reject, nil}
+  defp process_tag("mrf_tag:disable-any-subscription", %{"type" => "Follow"}),
+    do: {:reject, nil}
 
   defp process_tag(_, message), do: {:ok, message}
 
similarity index 86%
rename from lib/pleroma/web/activity_pub/mrf/user_allowlist.ex
rename to lib/pleroma/web/activity_pub/mrf/user_allowlist_policy.ex
index 47663414a38773e0b1a6e860ff6055a76f43d112..e35d2c422e1956971077397431c065da1c1944bf 100644 (file)
@@ -21,7 +21,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
   @impl true
   def filter(%{"actor" => actor} = object) do
     actor_info = URI.parse(actor)
-    allow_list = Config.get([:mrf_user_allowlist, String.to_atom(actor_info.host)], [])
+
+    allow_list =
+      Config.get(
+        [:mrf_user_allowlist, String.to_atom(actor_info.host)],
+        []
+      )
 
     filter_by_list(object, allow_list)
   end
index 543d4bb7d1dd89c7e03ec0df027bb4a78ba769fd..d14490bb59b88f65ba982ba7a7c88f0063c8952b 100644 (file)
@@ -641,7 +641,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   # an error or a tombstone.  This would allow us to verify that a deletion actually took
   # place.
   def handle_incoming(
-        %{"type" => "Delete", "object" => object_id, "actor" => _actor, "id" => _id} = data,
+        %{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => _id} = data,
         _options
       ) do
     object_id = Utils.get_ap_id(object_id)
@@ -653,7 +653,30 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
          {:ok, activity} <- ActivityPub.delete(object, false) do
       {:ok, activity}
     else
-      _e -> :error
+      nil ->
+        case User.get_cached_by_ap_id(object_id) do
+          %User{ap_id: ^actor} = user ->
+            {:ok, followers} = User.get_followers(user)
+
+            Enum.each(followers, fn follower ->
+              User.unfollow(follower, user)
+            end)
+
+            {:ok, friends} = User.get_friends(user)
+
+            Enum.each(friends, fn followed ->
+              User.unfollow(user, followed)
+            end)
+
+            User.invalidate_cache(user)
+            Repo.delete(user)
+
+          nil ->
+            :error
+        end
+
+      _e ->
+        :error
     end
   end
 
@@ -1064,6 +1087,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
         PleromaJobQueue.enqueue(:transmogrifier, __MODULE__, [:user_upgrade, user])
       end
 
+      if Pleroma.Config.get([:instance, :external_user_synchronization]) do
+        update_following_followers_counters(user)
+      end
+
       {:ok, user}
     else
       %User{} = user -> {:ok, user}
@@ -1096,4 +1123,27 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
     data
     |> maybe_fix_user_url
   end
+
+  def update_following_followers_counters(user) do
+    info = %{}
+
+    following = fetch_counter(user.following_address)
+    info = if following, do: Map.put(info, :following_count, following), else: info
+
+    followers = fetch_counter(user.follower_address)
+    info = if followers, do: Map.put(info, :follower_count, followers), else: info
+
+    User.set_info_cache(user, info)
+  end
+
+  defp fetch_counter(url) do
+    with {:ok, %{body: body, status: code}} when code in 200..299 <-
+           Pleroma.HTTP.get(
+             url,
+             [{:Accept, "application/activity+json"}]
+           ),
+         {:ok, data} <- Jason.decode(body) do
+      data["totalItems"]
+    end
+  end
 end
index 8965e3253de21d23591fb2743a76340d59e5b535..9908a2e75816151e7d203b7736a657a952230a5a 100644 (file)
@@ -1,3 +1,7 @@
+# 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.Visibility do
   alias Pleroma.Activity
   alias Pleroma.Object
index 0a2482a8c85c45224d8d2916f503f8778ed4c381..4a0bf482346782a5bd25010efe5b18bfb2edc949 100644 (file)
@@ -160,9 +160,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   end
 
   def right_add(conn, _) do
-    conn
-    |> put_status(404)
-    |> json(%{error: "No such permission_group"})
+    render_error(conn, :not_found, "No such permission_group")
   end
 
   def right_get(conn, %{"nickname" => nickname}) do
@@ -184,9 +182,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
       )
       when permission_group in ["moderator", "admin"] do
     if admin_nickname == nickname do
-      conn
-      |> put_status(403)
-      |> json(%{error: "You can't revoke your own admin status."})
+      render_error(conn, :forbidden, "You can't revoke your own admin status.")
     else
       user = User.get_cached_by_nickname(nickname)
 
@@ -207,9 +203,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   end
 
   def right_delete(conn, _) do
-    conn
-    |> put_status(404)
-    |> json(%{error: "No such permission_group"})
+    render_error(conn, :not_found, "No such permission_group")
   end
 
   def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do
@@ -377,13 +371,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
       if Pleroma.Config.get([:instance, :dynamic_configuration]) do
         updated =
           Enum.map(configs, fn
-            %{"group" => group, "key" => key, "value" => value} ->
-              {:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
-              config
-
             %{"group" => group, "key" => key, "delete" => "true"} ->
               {:ok, _} = Config.delete(%{group: group, key: key})
               nil
+
+            %{"group" => group, "key" => key, "value" => value} ->
+              {:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
+              config
           end)
           |> Enum.reject(&is_nil(&1))
 
@@ -401,26 +395,26 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
 
   def errors(conn, {:error, :not_found}) do
     conn
-    |> put_status(404)
-    |> json("Not found")
+    |> put_status(:not_found)
+    |> json(dgettext("errors", "Not found"))
   end
 
   def errors(conn, {:error, reason}) do
     conn
-    |> put_status(400)
+    |> put_status(:bad_request)
     |> json(reason)
   end
 
   def errors(conn, {:param_cast, _}) do
     conn
-    |> put_status(400)
-    |> json("Invalid parameters")
+    |> put_status(:bad_request)
+    |> json(dgettext("errors", "Invalid parameters"))
   end
 
   def errors(conn, _) do
     conn
-    |> put_status(500)
-    |> json("Something went wrong")
+    |> put_status(:internal_server_error)
+    |> json(dgettext("errors", "Something went wrong"))
   end
 
   defp page_params(params) do
index 8b9b658a9a0c8efbdd32d26a8e81888a9e39f12d..b4eb8e002a5de0a0fa0649c38317b624e43b7e54 100644 (file)
@@ -5,6 +5,7 @@
 defmodule Pleroma.Web.AdminAPI.Config do
   use Ecto.Schema
   import Ecto.Changeset
+  import Pleroma.Web.Gettext
   alias __MODULE__
   alias Pleroma.Repo
 
@@ -57,104 +58,95 @@ defmodule Pleroma.Web.AdminAPI.Config do
     with %Config{} = config <- Config.get_by_params(params) do
       Repo.delete(config)
     else
-      nil -> {:error, "Config with params #{inspect(params)} not found"}
+      nil ->
+        err =
+          dgettext("errors", "Config with params %{params} not found", params: inspect(params))
+
+        {:error, err}
     end
   end
 
   @spec from_binary(binary()) :: term()
-  def from_binary(value), do: :erlang.binary_to_term(value)
+  def from_binary(binary), do: :erlang.binary_to_term(binary)
 
-  @spec from_binary_to_map(binary()) :: any()
-  def from_binary_to_map(binary) do
+  @spec from_binary_with_convert(binary()) :: any()
+  def from_binary_with_convert(binary) do
     from_binary(binary)
     |> do_convert()
   end
 
-  defp do_convert([{k, v}] = value) when is_list(value) and length(value) == 1,
-    do: %{k => do_convert(v)}
+  defp do_convert(entity) when is_list(entity) do
+    for v <- entity, into: [], do: do_convert(v)
+  end
 
-  defp do_convert(values) when is_list(values), do: for(val <- values, do: do_convert(val))
+  defp do_convert(entity) when is_map(entity) do
+    for {k, v} <- entity, into: %{}, do: {do_convert(k), do_convert(v)}
+  end
 
-  defp do_convert({k, v} = value) when is_tuple(value),
-    do: %{k => do_convert(v)}
+  defp do_convert({:dispatch, [entity]}), do: %{"tuple" => [":dispatch", [inspect(entity)]]}
 
-  defp do_convert(value) when is_tuple(value), do: %{"tuple" => do_convert(Tuple.to_list(value))}
+  defp do_convert(entity) when is_tuple(entity),
+    do: %{"tuple" => do_convert(Tuple.to_list(entity))}
 
-  defp do_convert(value) when is_binary(value) or is_map(value) or is_number(value), do: value
+  defp do_convert(entity) when is_boolean(entity) or is_number(entity) or is_nil(entity),
+    do: entity
 
-  defp do_convert(value) when is_atom(value) do
-    string = to_string(value)
+  defp do_convert(entity) when is_atom(entity) do
+    string = to_string(entity)
 
     if String.starts_with?(string, "Elixir."),
-      do: String.trim_leading(string, "Elixir."),
-      else: value
+      do: do_convert(string),
+      else: ":" <> string
   end
 
-  @spec transform(any()) :: binary()
-  def transform(%{"tuple" => _} = entity), do: :erlang.term_to_binary(do_transform(entity))
+  defp do_convert("Elixir." <> module_name), do: module_name
 
-  def transform(entity) when is_map(entity) do
-    tuples =
-      for {k, v} <- entity,
-          into: [],
-          do: {if(is_atom(k), do: k, else: String.to_atom(k)), do_transform(v)}
+  defp do_convert(entity) when is_binary(entity), do: entity
 
-    Enum.reject(tuples, fn {_k, v} -> is_nil(v) end)
-    |> Enum.sort()
-    |> :erlang.term_to_binary()
-  end
-
-  def transform(entity) when is_list(entity) do
-    list = Enum.map(entity, &do_transform(&1))
-    :erlang.term_to_binary(list)
+  @spec transform(any()) :: binary()
+  def transform(entity) when is_binary(entity) or is_map(entity) or is_list(entity) do
+    :erlang.term_to_binary(do_transform(entity))
   end
 
   def transform(entity), do: :erlang.term_to_binary(entity)
 
-  defp do_transform(%Regex{} = value) when is_map(value), do: value
+  defp do_transform(%Regex{} = entity) when is_map(entity), do: entity
 
-  defp do_transform(%{"tuple" => [k, values] = entity}) when length(entity) == 2 do
-    {do_transform(k), do_transform(values)}
+  defp do_transform(%{"tuple" => [":dispatch", [entity]]}) do
+    cleaned_string = String.replace(entity, ~r/[^\w|^{:,[|^,|^[|^\]^}|^\/|^\.|^"]^\s/, "")
+    {dispatch_settings, []} = Code.eval_string(cleaned_string, [], requires: [], macros: [])
+    {:dispatch, [dispatch_settings]}
   end
 
-  defp do_transform(%{"tuple" => values}) do
-    Enum.reduce(values, {}, fn val, acc -> Tuple.append(acc, do_transform(val)) end)
+  defp do_transform(%{"tuple" => entity}) do
+    Enum.reduce(entity, {}, fn val, acc -> Tuple.append(acc, do_transform(val)) end)
   end
 
-  defp do_transform(value) when is_map(value) do
-    values = for {key, val} <- value, into: [], do: {String.to_atom(key), do_transform(val)}
-
-    Enum.sort(values)
+  defp do_transform(entity) when is_map(entity) do
+    for {k, v} <- entity, into: %{}, do: {do_transform(k), do_transform(v)}
   end
 
-  defp do_transform(value) when is_list(value) do
-    Enum.map(value, &do_transform(&1))
+  defp do_transform(entity) when is_list(entity) do
+    for v <- entity, into: [], do: do_transform(v)
   end
 
-  defp do_transform(entity) when is_list(entity) and length(entity) == 1, do: hd(entity)
-
-  defp do_transform(value) when is_binary(value) do
-    String.trim(value)
+  defp do_transform(entity) when is_binary(entity) do
+    String.trim(entity)
     |> do_transform_string()
   end
 
-  defp do_transform(value), do: value
-
-  defp do_transform_string(value) when byte_size(value) == 0, do: nil
-
-  defp do_transform_string(value) do
-    cond do
-      String.starts_with?(value, "Pleroma") or String.starts_with?(value, "Phoenix") ->
-        String.to_existing_atom("Elixir." <> value)
+  defp do_transform(entity), do: entity
 
-      String.starts_with?(value, ":") ->
-        String.replace(value, ":", "") |> String.to_existing_atom()
+  defp do_transform_string("~r/" <> pattern) do
+    pattern = String.trim_trailing(pattern, "/")
+    ~r/#{pattern}/
+  end
 
-      String.starts_with?(value, "i:") ->
-        String.replace(value, "i:", "") |> String.to_integer()
+  defp do_transform_string(":" <> atom), do: String.to_atom(atom)
 
-      true ->
-        value
-    end
+  defp do_transform_string(value) do
+    if String.starts_with?(value, "Pleroma") or String.starts_with?(value, "Phoenix"),
+      do: String.to_existing_atom("Elixir." <> value),
+      else: value
   end
 end
index 3ccc9ca46cd0d188a4f5204fd60bbdea1d25d17e..49add0b6e286a98e434daf8f617bc1f237a17e72 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.AdminAPI.ConfigView do
   use Pleroma.Web, :view
 
@@ -11,7 +15,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigView do
     %{
       key: config.key,
       group: config.group,
-      value: Pleroma.Web.AdminAPI.Config.from_binary_to_map(config.value)
+      value: Pleroma.Web.AdminAPI.Config.from_binary_with_convert(config.value)
     }
   end
 end
index f71c67a3d8383ac75bfb2cf3bce7c26e45501b85..f1450b1139178d940a78f854232613b67980ca9c 100644 (file)
@@ -13,6 +13,7 @@ defmodule Pleroma.Web.CommonAPI do
   alias Pleroma.Web.ActivityPub.Utils
   alias Pleroma.Web.ActivityPub.Visibility
 
+  import Pleroma.Web.Gettext
   import Pleroma.Web.CommonAPI.Utils
 
   def follow(follower, followed) do
@@ -74,7 +75,7 @@ defmodule Pleroma.Web.CommonAPI do
       {:ok, delete}
     else
       _ ->
-        {:error, "Could not delete"}
+        {:error, dgettext("errors", "Could not delete")}
     end
   end
 
@@ -85,7 +86,7 @@ defmodule Pleroma.Web.CommonAPI do
       ActivityPub.announce(user, object)
     else
       _ ->
-        {:error, "Could not repeat"}
+        {:error, dgettext("errors", "Could not repeat")}
     end
   end
 
@@ -95,7 +96,7 @@ defmodule Pleroma.Web.CommonAPI do
       ActivityPub.unannounce(user, object)
     else
       _ ->
-        {:error, "Could not unrepeat"}
+        {:error, dgettext("errors", "Could not unrepeat")}
     end
   end
 
@@ -106,7 +107,7 @@ defmodule Pleroma.Web.CommonAPI do
       ActivityPub.like(user, object)
     else
       _ ->
-        {:error, "Could not favorite"}
+        {:error, dgettext("errors", "Could not favorite")}
     end
   end
 
@@ -116,7 +117,7 @@ defmodule Pleroma.Web.CommonAPI do
       ActivityPub.unlike(user, object)
     else
       _ ->
-        {:error, "Could not unfavorite"}
+        {:error, dgettext("errors", "Could not unfavorite")}
     end
   end
 
@@ -148,10 +149,10 @@ defmodule Pleroma.Web.CommonAPI do
       object = Object.get_cached_by_ap_id(object.data["id"])
       {:ok, answer_activities, object}
     else
-      {:author, _} -> {:error, "Poll's author can't vote"}
-      {:existing_votes, _} -> {:error, "Already voted"}
-      {:choice_check, {_, false}} -> {:error, "Invalid indices"}
-      {:count_check, false} -> {:error, "Too many choices"}
+      {:author, _} -> {:error, dgettext("errors", "Poll's author can't vote")}
+      {:existing_votes, _} -> {:error, dgettext("errors", "Already voted")}
+      {:choice_check, {_, false}} -> {:error, dgettext("errors", "Invalid indices")}
+      {:count_check, false} -> {:error, dgettext("errors", "Too many choices")}
     end
   end
 
@@ -248,9 +249,14 @@ defmodule Pleroma.Web.CommonAPI do
 
       res
     else
-      {:private_to_public, true} -> {:error, "The message visibility must be direct"}
-      {:error, _} = e -> e
-      e -> {:error, e}
+      {:private_to_public, true} ->
+        {:error, dgettext("errors", "The message visibility must be direct")}
+
+      {:error, _} = e ->
+        e
+
+      e ->
+        {:error, e}
     end
   end
 
@@ -301,7 +307,7 @@ defmodule Pleroma.Web.CommonAPI do
         {:error, err}
 
       _ ->
-        {:error, "Could not pin"}
+        {:error, dgettext("errors", "Could not pin")}
     end
   end
 
@@ -318,7 +324,7 @@ defmodule Pleroma.Web.CommonAPI do
         {:error, err}
 
       _ ->
-        {:error, "Could not unpin"}
+        {:error, dgettext("errors", "Could not unpin")}
     end
   end
 
@@ -326,7 +332,7 @@ defmodule Pleroma.Web.CommonAPI do
     with {:ok, _} <- ThreadMute.add_mute(user.id, activity.data["context"]) do
       {:ok, activity}
     else
-      {:error, _} -> {:error, "conversation is already muted"}
+      {:error, _} -> {:error, dgettext("errors", "conversation is already muted")}
     end
   end
 
@@ -371,8 +377,8 @@ defmodule Pleroma.Web.CommonAPI do
       {:ok, activity}
     else
       {:error, err} -> {:error, err}
-      {:account_id, %{}} -> {:error, "Valid `account_id` required"}
-      {:account, nil} -> {:error, "Account not found"}
+      {:account_id, %{}} -> {:error, dgettext("errors", "Valid `account_id` required")}
+      {:account, nil} -> {:error, dgettext("errors", "Account not found")}
     end
   end
 
@@ -381,14 +387,9 @@ defmodule Pleroma.Web.CommonAPI do
          {:ok, activity} <- Utils.update_report_state(activity, state) do
       {:ok, activity}
     else
-      nil ->
-        {:error, :not_found}
-
-      {:error, reason} ->
-        {:error, reason}
-
-      _ ->
-        {:error, "Could not update state"}
+      nil -> {:error, :not_found}
+      {:error, reason} -> {:error, reason}
+      _ -> {:error, dgettext("errors", "Could not update state")}
     end
   end
 
@@ -398,11 +399,8 @@ defmodule Pleroma.Web.CommonAPI do
          {:ok, activity} <- set_visibility(activity, opts) do
       {:ok, activity}
     else
-      nil ->
-        {:error, :not_found}
-
-      {:error, reason} ->
-        {:error, reason}
+      nil -> {:error, :not_found}
+      {:error, reason} -> {:error, reason}
     end
   end
 
index 8b9477927c84c3b5ae7f094a764810863358b88b..8e482eef7b88f9e9520e40e70b039598e0ee3d01 100644 (file)
@@ -3,6 +3,8 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.CommonAPI.Utils do
+  import Pleroma.Web.Gettext
+
   alias Calendar.Strftime
   alias Comeonin.Pbkdf2
   alias Pleroma.Activity
@@ -372,7 +374,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
          true <- Pbkdf2.checkpw(password, db_user.password_hash) do
       {:ok, db_user}
     else
-      _ -> {:error, "Invalid password."}
+      _ -> {:error, dgettext("errors", "Invalid password.")}
     end
   end
 
@@ -455,7 +457,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
     if String.length(comment) <= max_size do
       {:ok, format_input(comment, "text/plain")}
     else
-      {:error, "Comment must be up to #{max_size} characters"}
+      {:error,
+       dgettext("errors", "Comment must be up to %{max_size} characters", max_size: max_size)}
     end
   end
 
@@ -490,7 +493,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
       context
     else
       _e ->
-        {:error, "No such conversation"}
+        {:error, dgettext("errors", "No such conversation")}
     end
   end
 
@@ -512,10 +515,10 @@ defmodule Pleroma.Web.CommonAPI.Utils do
       if length > 0 or Enum.count(attachments) > 0 do
         :ok
       else
-        {:error, "Cannot post an empty status without attachments"}
+        {:error, dgettext("errors", "Cannot post an empty status without attachments")}
       end
     else
-      {:error, "The status is over the character limit"}
+      {:error, dgettext("errors", "The status is over the character limit")}
     end
   end
 end
index ddaf88f1d8630e6eb3fd4afd0aa941931cb90249..c123530dcf961c10a2dfd5179404c3a43c53613f 100644 (file)
@@ -7,13 +7,9 @@ defmodule Pleroma.Web.Endpoint do
 
   socket("/socket", Pleroma.Web.UserSocket)
 
-  # Serve at "/" the static files from "priv/static" directory.
-  #
-  # You should set gzip to true if you are running phoenix.digest
-  # when deploying your static files in production.
+  plug(Pleroma.Plugs.SetLocalePlug)
   plug(CORSPlug)
   plug(Pleroma.Plugs.HTTPSecurityPlug)
-
   plug(Pleroma.Plugs.UploadedMedia)
 
   @static_cache_control "public, no-cache"
@@ -30,6 +26,10 @@ defmodule Pleroma.Web.Endpoint do
     }
   )
 
+  # Serve at "/" the static files from "priv/static" directory.
+  #
+  # You should set gzip to true if you are running phoenix.digest
+  # when deploying your static files in production.
   plug(
     Plug.Static,
     at: "/",
index 3a3ec7c2a05077054afa2e53fc719a554b9d3840..c82b201233e31b5c1079fd7df8d28e0a1c667694 100644 (file)
@@ -1,3 +1,7 @@
+# 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.MastodonAPI do
   import Ecto.Query
   import Ecto.Changeset
index 0d3a878bb11511190c30b758a45cfa99fe02193d..8c2033c3ab433ad8650638842e7c3c1b0ce8f076 100644 (file)
@@ -160,10 +160,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
         AccountView.render("account.json", %{user: user, for: user, with_pleroma_settings: true})
       )
     else
-      _e ->
-        conn
-        |> put_status(403)
-        |> json(%{error: "Invalid request"})
+      _e -> render_error(conn, :forbidden, "Invalid request")
     end
   end
 
@@ -258,10 +255,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
       account = AccountView.render("account.json", %{user: user, for: for_user})
       json(conn, account)
     else
-      _e ->
-        conn
-        |> put_status(404)
-        |> json(%{error: "Can't find user"})
+      _e -> render_error(conn, :not_found, "Can't find user")
     end
   end
 
@@ -305,7 +299,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
         "static_url" => url,
         "visible_in_picker" => true,
         "url" => url,
-        "tags" => tags
+        "tags" => tags,
+        # Assuming that a comma is authorized in the category name
+        "category" => (tags -- ["Custom"]) |> Enum.join(",")
       }
     end)
   end
@@ -509,15 +505,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
       |> put_view(StatusView)
       |> try_render("poll.json", %{object: object, for: user})
     else
-      nil ->
-        conn
-        |> put_status(404)
-        |> json(%{error: "Record not found"})
-
-      false ->
-        conn
-        |> put_status(404)
-        |> json(%{error: "Record not found"})
+      nil -> render_error(conn, :not_found, "Record not found")
+      false -> render_error(conn, :not_found, "Record not found")
     end
   end
 
@@ -546,18 +535,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
       |> try_render("poll.json", %{object: object, for: user})
     else
       nil ->
-        conn
-        |> put_status(404)
-        |> json(%{error: "Record not found"})
+        render_error(conn, :not_found, "Record not found")
 
       false ->
-        conn
-        |> put_status(404)
-        |> json(%{error: "Record not found"})
+        render_error(conn, :not_found, "Record not found")
 
       {:error, message} ->
         conn
-        |> put_status(422)
+        |> put_status(:unprocessable_entity)
         |> json(%{error: message})
     end
   end
@@ -646,10 +631,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
       json(conn, %{})
     else
-      _e ->
-        conn
-        |> put_status(403)
-        |> json(%{error: "Can't delete this post"})
+      _e -> render_error(conn, :forbidden, "Can't delete this post")
     end
   end
 
@@ -697,8 +679,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     else
       {:error, reason} ->
         conn
-        |> put_resp_content_type("application/json")
-        |> send_resp(:bad_request, Jason.encode!(%{"error" => reason}))
+        |> put_status(:bad_request)
+        |> json(%{"error" => reason})
     end
   end
 
@@ -774,8 +756,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     else
       {:error, reason} ->
         conn
-        |> put_resp_content_type("application/json")
-        |> send_resp(403, Jason.encode!(%{"error" => reason}))
+        |> put_status(:forbidden)
+        |> json(%{"error" => reason})
     end
   end
 
@@ -790,8 +772,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     else
       {:error, reason} ->
         conn
-        |> put_resp_content_type("application/json")
-        |> send_resp(403, Jason.encode!(%{"error" => reason}))
+        |> put_status(:forbidden)
+        |> json(%{"error" => reason})
     end
   end
 
@@ -869,9 +851,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
         conn
         |> json(rendered)
       else
-        conn
-        |> put_resp_content_type("application/json")
-        |> send_resp(415, Jason.encode!(%{"error" => "mascots can only be images"}))
+        render_error(conn, :unsupported_media_type, "mascots can only be images")
       end
     end
   end
@@ -1000,8 +980,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     else
       {:error, message} ->
         conn
-        |> put_resp_content_type("application/json")
-        |> send_resp(403, Jason.encode!(%{"error" => message}))
+        |> put_status(:forbidden)
+        |> json(%{error: message})
     end
   end
 
@@ -1014,8 +994,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     else
       {:error, message} ->
         conn
-        |> put_resp_content_type("application/json")
-        |> send_resp(403, Jason.encode!(%{"error" => message}))
+        |> put_status(:forbidden)
+        |> json(%{error: message})
     end
   end
 
@@ -1032,8 +1012,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
 
       {:error, message} ->
         conn
-        |> put_resp_content_type("application/json")
-        |> send_resp(403, Jason.encode!(%{"error" => message}))
+        |> put_status(:forbidden)
+        |> json(%{error: message})
     end
   end
 
@@ -1050,8 +1030,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
 
       {:error, message} ->
         conn
-        |> put_resp_content_type("application/json")
-        |> send_resp(403, Jason.encode!(%{"error" => message}))
+        |> put_status(:forbidden)
+        |> json(%{error: message})
     end
   end
 
@@ -1080,8 +1060,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     else
       {:error, message} ->
         conn
-        |> put_resp_content_type("application/json")
-        |> send_resp(403, Jason.encode!(%{"error" => message}))
+        |> put_status(:forbidden)
+        |> json(%{error: message})
     end
   end
 
@@ -1094,8 +1074,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     else
       {:error, message} ->
         conn
-        |> put_resp_content_type("application/json")
-        |> send_resp(403, Jason.encode!(%{"error" => message}))
+        |> put_status(:forbidden)
+        |> json(%{error: message})
     end
   end
 
@@ -1116,8 +1096,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     else
       {:error, message} ->
         conn
-        |> put_resp_content_type("application/json")
-        |> send_resp(403, Jason.encode!(%{"error" => message}))
+        |> put_status(:forbidden)
+        |> json(%{error: message})
     end
   end
 
@@ -1131,8 +1111,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     else
       {:error, message} ->
         conn
-        |> put_resp_content_type("application/json")
-        |> send_resp(403, Jason.encode!(%{"error" => message}))
+        |> put_status(:forbidden)
+        |> json(%{error: message})
     end
   end
 
@@ -1166,8 +1146,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     else
       {:error, message} ->
         conn
-        |> put_resp_content_type("application/json")
-        |> send_resp(403, Jason.encode!(%{"error" => message}))
+        |> put_status(:forbidden)
+        |> json(%{error: message})
     end
   end
 
@@ -1180,8 +1160,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     else
       {:error, message} ->
         conn
-        |> put_resp_content_type("application/json")
-        |> send_resp(403, Jason.encode!(%{"error" => message}))
+        |> put_status(:forbidden)
+        |> json(%{error: message})
     end
   end
 
@@ -1229,13 +1209,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
       |> put_view(StatusView)
       |> render("index.json", %{activities: activities, for: for_user, as: :activity})
     else
-      nil ->
-        {:error, :not_found}
-
-      true ->
-        conn
-        |> put_status(403)
-        |> json(%{error: "Can't get favorites"})
+      nil -> {:error, :not_found}
+      true -> render_error(conn, :forbidden, "Can't get favorites")
     end
   end
 
@@ -1267,10 +1242,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
       res = ListView.render("list.json", list: list)
       json(conn, res)
     else
-      _e ->
-        conn
-        |> put_status(404)
-        |> json(%{error: "Record not found"})
+      _e -> render_error(conn, :not_found, "Record not found")
     end
   end
 
@@ -1286,7 +1258,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
       json(conn, %{})
     else
       _e ->
-        json(conn, "error")
+        json(conn, dgettext("errors", "error"))
     end
   end
 
@@ -1337,7 +1309,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
       json(conn, res)
     else
       _e ->
-        json(conn, "error")
+        json(conn, dgettext("errors", "error"))
     end
   end
 
@@ -1361,10 +1333,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
       |> put_view(StatusView)
       |> render("index.json", %{activities: activities, for: user, as: :activity})
     else
-      _e ->
-        conn
-        |> put_status(403)
-        |> json(%{error: "Error."})
+      _e -> render_error(conn, :forbidden, "Error.")
     end
   end
 
@@ -1483,8 +1452,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     else
       e ->
         conn
-        |> put_resp_content_type("application/json")
-        |> send_resp(500, Jason.encode!(%{"error" => inspect(e)}))
+        |> put_status(:internal_server_error)
+        |> json(%{error: inspect(e)})
     end
   end
 
@@ -1652,20 +1621,18 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
       |> Enum.map_join(", ", fn {_k, v} -> v end)
 
     conn
-    |> put_status(422)
+    |> put_status(:unprocessable_entity)
     |> json(%{error: error_message})
   end
 
   def errors(conn, {:error, :not_found}) do
-    conn
-    |> put_status(404)
-    |> json(%{error: "Record not found"})
+    render_error(conn, :not_found, "Record not found")
   end
 
   def errors(conn, _) do
     conn
-    |> put_status(500)
-    |> json("Something went wrong")
+    |> put_status(:internal_server_error)
+    |> json(dgettext("errors", "Something went wrong"))
   end
 
   def suggestions(%{assigns: %{user: user}} = conn, _) do
@@ -1785,21 +1752,17 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     else
       {:error, errors} ->
         conn
-        |> put_status(400)
-        |> json(Jason.encode!(errors))
+        |> put_status(:bad_request)
+        |> json(errors)
     end
   end
 
   def account_register(%{assigns: %{app: _app}} = conn, _params) do
-    conn
-    |> put_status(400)
-    |> json(%{error: "Missing parameters"})
+    render_error(conn, :bad_request, "Missing parameters")
   end
 
   def account_register(conn, _) do
-    conn
-    |> put_status(403)
-    |> json(%{error: "Invalid credentials"})
+    render_error(conn, :forbidden, "Invalid credentials")
   end
 
   def conversations(%{assigns: %{user: user}} = conn, params) do
@@ -1829,21 +1792,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
 
   def try_render(conn, target, params)
       when is_binary(target) do
-    res = render(conn, target, params)
-
-    if res == nil do
-      conn
-      |> put_status(501)
-      |> json(%{error: "Can't display this activity"})
-    else
-      res
+    case render(conn, target, params) do
+      nil -> render_error(conn, :not_implemented, "Can't display this activity")
+      res -> res
     end
   end
 
   def try_render(conn, _, _) do
-    conn
-    |> put_status(501)
-    |> json(%{error: "Can't display this activity"})
+    render_error(conn, :not_implemented, "Can't display this activity")
   end
 
   defp present?(nil), do: false
index efa9cc78824eb00add3735568e57a89943b5d69e..9072aa7a47c1a3702b8ae46b45cfd676be57d30a 100644 (file)
@@ -4,61 +4,18 @@
 
 defmodule Pleroma.Web.MastodonAPI.SearchController do
   use Pleroma.Web, :controller
+
   alias Pleroma.Activity
+  alias Pleroma.Plugs.RateLimiter
+  alias Pleroma.Repo
   alias Pleroma.User
   alias Pleroma.Web
+  alias Pleroma.Web.ControllerHelper
   alias Pleroma.Web.MastodonAPI.AccountView
   alias Pleroma.Web.MastodonAPI.StatusView
 
-  alias Pleroma.Web.ControllerHelper
-
   require Logger
-
-  plug(Pleroma.Plugs.RateLimiter, :search when action in [:search, :search2, :account_search])
-
-  def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
-    accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end, [])
-    statuses = with_fallback(fn -> Activity.search(user, query) end, [])
-    tags_path = Web.base_url() <> "/tag/"
-
-    tags =
-      query
-      |> String.split()
-      |> Enum.uniq()
-      |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
-      |> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
-      |> Enum.map(fn tag -> %{name: tag, url: tags_path <> tag} end)
-
-    res = %{
-      "accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
-      "statuses" =>
-        StatusView.render("index.json", activities: statuses, for: user, as: :activity),
-      "hashtags" => tags
-    }
-
-    json(conn, res)
-  end
-
-  def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
-    accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end, [])
-    statuses = with_fallback(fn -> Activity.search(user, query) end, [])
-
-    tags =
-      query
-      |> String.split()
-      |> Enum.uniq()
-      |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
-      |> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
-
-    res = %{
-      "accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
-      "statuses" =>
-        StatusView.render("index.json", activities: statuses, for: user, as: :activity),
-      "hashtags" => tags
-    }
-
-    json(conn, res)
-  end
+  plug(RateLimiter, :search when action in [:search, :search2, :account_search])
 
   def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
     accounts = User.search(query, search_options(params, user))
@@ -67,17 +24,86 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
     json(conn, res)
   end
 
+  def search2(conn, params), do: do_search(:v2, conn, params)
+  def search(conn, params), do: do_search(:v1, conn, params)
+
+  defp do_search(version, %{assigns: %{user: user}} = conn, %{"q" => query} = params) do
+    options = search_options(params, user)
+    timeout = Keyword.get(Repo.config(), :timeout, 15_000)
+    default_values = %{"statuses" => [], "accounts" => [], "hashtags" => []}
+
+    result =
+      default_values
+      |> Enum.map(fn {resource, default_value} ->
+        if params["type"] == nil or params["type"] == resource do
+          {resource, fn -> resource_search(version, resource, query, options) end}
+        else
+          {resource, fn -> default_value end}
+        end
+      end)
+      |> Task.async_stream(fn {resource, f} -> {resource, with_fallback(f)} end,
+        timeout: timeout,
+        on_timeout: :kill_task
+      )
+      |> Enum.reduce(default_values, fn
+        {:ok, {resource, result}}, acc ->
+          Map.put(acc, resource, result)
+
+        _error, acc ->
+          acc
+      end)
+
+    json(conn, result)
+  end
+
   defp search_options(params, user) do
     [
       resolve: params["resolve"] == "true",
       following: params["following"] == "true",
       limit: ControllerHelper.fetch_integer_param(params, "limit"),
       offset: ControllerHelper.fetch_integer_param(params, "offset"),
+      type: params["type"],
+      author: get_author(params),
       for_user: user
     ]
+    |> Enum.filter(&elem(&1, 1))
+  end
+
+  defp resource_search(_, "accounts", query, options) do
+    accounts = with_fallback(fn -> User.search(query, options) end)
+    AccountView.render("accounts.json", users: accounts, for: options[:for_user], as: :user)
+  end
+
+  defp resource_search(_, "statuses", query, options) do
+    statuses = with_fallback(fn -> Activity.search(options[:for_user], query, options) end)
+    StatusView.render("index.json", activities: statuses, for: options[:for_user], as: :activity)
+  end
+
+  defp resource_search(:v2, "hashtags", query, _options) do
+    tags_path = Web.base_url() <> "/tag/"
+
+    query
+    |> prepare_tags()
+    |> Enum.map(fn tag ->
+      tag = String.trim_leading(tag, "#")
+      %{name: tag, url: tags_path <> tag}
+    end)
+  end
+
+  defp resource_search(:v1, "hashtags", query, _options) do
+    query
+    |> prepare_tags()
+    |> Enum.map(fn tag -> String.trim_leading(tag, "#") end)
   end
 
-  defp with_fallback(f, fallback) do
+  defp prepare_tags(query) do
+    query
+    |> String.split()
+    |> Enum.uniq()
+    |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
+  end
+
+  defp with_fallback(f, fallback \\ []) do
     try do
       f.()
     rescue
@@ -86,4 +112,9 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
         fallback
     end
   end
+
+  defp get_author(%{"account_id" => account_id}) when is_binary(account_id),
+    do: User.get_cached_by_id(account_id)
+
+  defp get_author(_params), do: nil
 end
index b6c8ff808ba5888532bd9f9e24cd0d305c63edeb..255ee2f18bd9d941748ed3e43b16718a5ceffb23 100644 (file)
@@ -59,13 +59,13 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
   #
   def errors(conn, {:error, :not_found}) do
     conn
-    |> put_status(404)
-    |> json("Not found")
+    |> put_status(:not_found)
+    |> json(dgettext("errors", "Not found"))
   end
 
   def errors(conn, _) do
     conn
-    |> put_status(500)
-    |> json("Something went wrong")
+    |> put_status(:internal_server_error)
+    |> json(dgettext("errors", "Something went wrong"))
   end
 end
index af1dcf66dd7588856551d2bee1e8f26b3ace7ca2..38bdec737fb4bdf38462833b8ec9778b9a6486d8 100644 (file)
@@ -1,3 +1,7 @@
+# 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.ConversationView do
   use Pleroma.Web, :view
 
index ec582b919c626aa5c8f1e9330a03c0d1afafac8d..06a7251d8f959e1ce1deca2c3ae2c0e8e809bbec 100644 (file)
@@ -19,6 +19,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
   import Pleroma.Web.ActivityPub.Visibility, only: [get_visibility: 1]
 
   # TODO: Add cached version.
+  defp get_replied_to_activities([]), do: %{}
+
   defp get_replied_to_activities(activities) do
     activities
     |> Enum.map(fn
@@ -147,8 +149,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
     tags = object.data["tag"] || []
     sensitive = object.data["sensitive"] || Enum.member?(tags, "nsfw")
 
+    tag_mentions =
+      tags
+      |> Enum.filter(fn tag -> is_map(tag) and tag["type"] == "Mention" end)
+      |> Enum.map(fn tag -> tag["href"] end)
+
     mentions =
-      activity.recipients
+      (object.data["to"] ++ tag_mentions)
+      |> Enum.uniq()
       |> Enum.map(fn 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)
index e9a8cfc8dd24462a66998c5c48fa2fd9895709d7..4289ebdbdc5b24b2e54997f77763a69576712638 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.Metadata.PlayerView do
   use Pleroma.Web, :view
   import Phoenix.HTML.Tag, only: [content_tag: 3, tag: 2]
index 03af899c4799a5f6fa1559d02b2f12ad49a2168c..f87fc197364f2588d6510ca0237ae1ffe5c7570c 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.Metadata.Providers.RelMe do
   alias Pleroma.Web.Metadata.Providers.Provider
   @behaviour Provider
index 489d5d3a528dc89d6f4a7600055c8249fc4ba856..b786a521b2c4018d41eeaae0bf18a0d44a09b543 100644 (file)
@@ -29,7 +29,7 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do
     else
       false ->
         conn
-        |> put_status(403)
+        |> put_status(:forbidden)
         |> json(false)
 
       _ ->
index 869dda5c5cd3d44eac41aa853ff23d92b1332e8e..cd9a4f4a8c479816b76a6d875ac0b1d96935f034 100644 (file)
@@ -201,8 +201,6 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
   end
 
   def nodeinfo(conn, _) do
-    conn
-    |> put_status(404)
-    |> json(%{error: "Nodeinfo schema version not handled"})
+    render_error(conn, :not_found, "Nodeinfo schema version not handled")
   end
 end
index e3984f0099b6d54d2504f7fe1304032af0eb1205..dd7f08bf16f7b0b794b1fe87409ad8dc5f42ac73 100644 (file)
@@ -9,21 +9,24 @@ defmodule Pleroma.Web.OAuth.FallbackController do
   def call(conn, {:register, :generic_error}) do
     conn
     |> put_status(:internal_server_error)
-    |> put_flash(:error, "Unknown error, please check the details and try again.")
+    |> put_flash(
+      :error,
+      dgettext("errors", "Unknown error, please check the details and try again.")
+    )
     |> OAuthController.registration_details(conn.params)
   end
 
   def call(conn, {:register, _error}) do
     conn
     |> put_status(:unauthorized)
-    |> put_flash(:error, "Invalid Username/Password")
+    |> put_flash(:error, dgettext("errors", "Invalid Username/Password"))
     |> OAuthController.registration_details(conn.params)
   end
 
   def call(conn, _error) do
     conn
     |> put_status(:unauthorized)
-    |> put_flash(:error, "Invalid Username/Password")
+    |> put_flash(:error, dgettext("errors", "Invalid Username/Password"))
     |> OAuthController.authorize(conn.params)
   end
 end
index 3f8e3b0747c927d8510b638005bec0ed2535c373..ef53b7ae348c506eace423b7dae2399b1f0ced40 100644 (file)
@@ -90,7 +90,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
       redirect(conn, external: url)
     else
       conn
-      |> put_flash(:error, "Unlisted redirect_uri.")
+      |> put_flash(:error, dgettext("errors", "Unlisted redirect_uri."))
       |> redirect(external: redirect_uri(conn, redirect_uri))
     end
   end
@@ -128,7 +128,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
       redirect(conn, external: url)
     else
       conn
-      |> put_flash(:error, "Unlisted redirect_uri.")
+      |> put_flash(:error, dgettext("errors", "Unlisted redirect_uri."))
       |> redirect(external: redirect_uri(conn, redirect_uri))
     end
   end
@@ -142,7 +142,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
     # Per https://github.com/tootsuite/mastodon/blob/
     #   51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L39
     conn
-    |> put_flash(:error, "This action is outside the authorized scopes")
+    |> put_flash(:error, dgettext("errors", "This action is outside the authorized scopes"))
     |> put_status(:unauthorized)
     |> authorize(params)
   end
@@ -155,7 +155,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
     # Per https://github.com/tootsuite/mastodon/blob/
     #   51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76
     conn
-    |> put_flash(:error, "Your login is missing a confirmed e-mail address")
+    |> put_flash(:error, dgettext("errors", "Your login is missing a confirmed e-mail address"))
     |> put_status(:forbidden)
     |> authorize(params)
   end
@@ -176,9 +176,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
 
       json(conn, Token.Response.build(user, token, response_attrs))
     else
-      _error ->
-        put_status(conn, 400)
-        |> json(%{error: "Invalid credentials"})
+      _error -> render_invalid_credentials_error(conn)
     end
   end
 
@@ -192,9 +190,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
 
       json(conn, Token.Response.build(user, token, response_attrs))
     else
-      _error ->
-        put_status(conn, 400)
-        |> json(%{error: "Invalid credentials"})
+      _error -> render_invalid_credentials_error(conn)
     end
   end
 
@@ -214,18 +210,13 @@ defmodule Pleroma.Web.OAuth.OAuthController do
       {:auth_active, false} ->
         # Per https://github.com/tootsuite/mastodon/blob/
         #   51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76
-        conn
-        |> put_status(:forbidden)
-        |> json(%{error: "Your login is missing a confirmed e-mail address"})
+        render_error(conn, :forbidden, "Your login is missing a confirmed e-mail address")
 
       {:user_active, false} ->
-        conn
-        |> put_status(:forbidden)
-        |> json(%{error: "Your account is currently disabled"})
+        render_error(conn, :forbidden, "Your account is currently disabled")
 
       _error ->
-        put_status(conn, 400)
-        |> json(%{error: "Invalid credentials"})
+        render_invalid_credentials_error(conn)
     end
   end
 
@@ -247,9 +238,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
          {:ok, token} <- Token.exchange_token(app, auth) do
       json(conn, Token.Response.build_for_client_credentials(token))
     else
-      _error ->
-        put_status(conn, 400)
-        |> json(%{error: "Invalid credentials"})
+      _error -> render_invalid_credentials_error(conn)
     end
   end
 
@@ -271,9 +260,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
 
   # Response for bad request
   defp bad_request(%Plug.Conn{} = conn, _) do
-    conn
-    |> put_status(500)
-    |> json(%{error: "Bad request"})
+    render_error(conn, :internal_server_error, "Bad request")
   end
 
   @doc "Prepares OAuth request to provider for Ueberauth"
@@ -304,9 +291,11 @@ defmodule Pleroma.Web.OAuth.OAuthController do
   def request(%Plug.Conn{} = conn, params) do
     message =
       if params["provider"] do
-        "Unsupported OAuth provider: #{params["provider"]}."
+        dgettext("errors", "Unsupported OAuth provider: %{provider}.",
+          provider: params["provider"]
+        )
       else
-        "Bad OAuth request."
+        dgettext("errors", "Bad OAuth request.")
       end
 
     conn
@@ -320,7 +309,10 @@ defmodule Pleroma.Web.OAuth.OAuthController do
     message = Enum.join(messages, "; ")
 
     conn
-    |> put_flash(:error, "Failed to authenticate: #{message}.")
+    |> put_flash(
+      :error,
+      dgettext("errors", "Failed to authenticate: %{message}.", message: message)
+    )
     |> redirect(external: redirect_uri(conn, params["redirect_uri"]))
   end
 
@@ -350,7 +342,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
         Logger.debug(inspect(["OAUTH_ERROR", error, conn.assigns]))
 
         conn
-        |> put_flash(:error, "Failed to set up user account.")
+        |> put_flash(:error, dgettext("errors", "Failed to set up user account."))
         |> redirect(external: redirect_uri(conn, params["redirect_uri"]))
     end
   end
@@ -468,4 +460,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do
     |> String.split()
     |> Enum.at(0)
   end
+
+  defp render_invalid_credentials_error(conn) do
+    render_error(conn, :bad_request, "Invalid credentials")
+  end
 end
index 2648571add3d86006665f14e888fa7173ea21fcf..26611081439b75772fd32262d2cb94bdbac3caf8 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.OAuth.Token.Response do
   @moduledoc false
 
index 7df0be14e93d27766456b0cc2aef67f4c73ae08e..c620050c87e407491875b595f99bb6a415c0c22d 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.OAuth.Token.Strategy.RefreshToken do
   @moduledoc """
   Functions for dealing with refresh token strategy.
index dea63ca5451711293b5c442fe3172b4fd9bca26a..983f095b4ecb9e70d78e53866d4d4d8ccf83cf60 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.OAuth.Token.Strategy.Revoke do
   @moduledoc """
   Functions for dealing with revocation.
index 7a4fddafd947cb098d3d56d70f6a900244943cc9..1e8765e93bfb25ebbab982b3cd5a216a9ecda8ad 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.OAuth.Token.Utils do
   @moduledoc """
   Auxiliary functions for dealing with tokens.
index 2fb6ce41b2ffe30804931722a60ad34342c00ab3..372d528991ab6b01fa7cb3d72f99814574cc421e 100644 (file)
@@ -245,14 +245,10 @@ defmodule Pleroma.Web.OStatus.OStatusController do
   end
 
   def errors(conn, {:error, :not_found}) do
-    conn
-    |> put_status(404)
-    |> text("Not found")
+    render_error(conn, :not_found, "Not found")
   end
 
   def errors(conn, _) do
-    conn
-    |> put_status(500)
-    |> text("Something went wrong")
+    render_error(conn, :internal_server_error, "Something went wrong")
   end
 end
index fb79630e411a3bb8955c8404ccf56a5d9248928b..9139756160034983ad6c63721e7684de8ee9a7f2 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do
   def parse(html, data, prefix, error_message, key_name, value_name \\ "content") do
     meta_data =
index 2530b8c9d0b29bff8d454c79eeb066b0150d8c2e..875637c4d9d3fe509544e47103dd93594a1c6ed2 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do
   def parse(html, _data) do
     with elements = [_ | _] <- get_discovery_data(html),
index 0e1a0e7199452e3763b5320fafd0c54c1b99c3b6..d40fa009fa021ac63cb1b26b05a8e72376e59b67 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.RichMedia.Parsers.OGP do
   def parse(html, data) do
     Pleroma.Web.RichMedia.Parsers.MetaTagsParser.parse(
index a317c3e78f1dbfc646e382762ae31457a094bcc4..e4efe2dd0573b745f8404039ffb0f9f7c8eddcb6 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.RichMedia.Parsers.TwitterCard do
   def parse(html, data) do
     Pleroma.Web.RichMedia.Parsers.MetaTagsParser.parse(
diff --git a/lib/pleroma/web/translation_helpers.ex b/lib/pleroma/web/translation_helpers.ex
new file mode 100644 (file)
index 0000000..8f5a43b
--- /dev/null
@@ -0,0 +1,17 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.TranslationHelpers do
+  defmacro render_error(conn, status, msgid, bindings \\ Macro.escape(%{})) do
+    quote do
+      require Pleroma.Web.Gettext
+
+      unquote(conn)
+      |> Plug.Conn.put_status(unquote(status))
+      |> Phoenix.Controller.json(%{
+        error: Pleroma.Web.Gettext.dgettext("errors", unquote(msgid), unquote(bindings))
+      })
+    end
+  end
+end
index 5d8a77346fb6d3f299d8300206f2061fe61840c6..bf09775e6e79ffa383af21597940bed005f237bb 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.UploaderController do
   use Pleroma.Web, :controller
 
@@ -8,7 +12,7 @@ defmodule Pleroma.Web.UploaderController do
   end
 
   def callbacks(conn, _) do
-    send_resp(conn, 400, "bad request")
+    render_error(conn, :bad_request, "bad request")
   end
 
   defp process_callback(conn, pid, params) when is_pid(pid) do
@@ -20,6 +24,6 @@ defmodule Pleroma.Web.UploaderController do
   end
 
   defp process_callback(conn, _, _) do
-    send_resp(conn, 400, "bad request")
+    render_error(conn, :bad_request, "bad request")
   end
 end
index 66813e4ddaffa5ad1fd4f807047e639c86a29cc0..b42f6887e17b090535f9170f6703ee8ca9b6f2fa 100644 (file)
@@ -23,9 +23,11 @@ defmodule Pleroma.Web do
   def controller do
     quote do
       use Phoenix.Controller, namespace: Pleroma.Web
+
       import Plug.Conn
       import Pleroma.Web.Gettext
       import Pleroma.Web.Router.Helpers
+      import Pleroma.Web.TranslationHelpers
 
       plug(:set_put_layout)
 
index 42f645b21d81671ebbd20ec4d4964db398e92e47..9f3fc535dc9d8dbb33e3e90a50967892b80294eb 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Phoenix.Transports.WebSocket.Raw do
   import Plug.Conn,
     only: [
index b58602c7b7592f4ef3fa9ab218deaa90afbb05e6..ceeef2755ec57688c63809bab55546a0107fd1bd 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.XmlBuilder do
   def to_xml({tag, attributes, content}) do
     open_tag = make_open_tag(tag, attributes)
diff --git a/mix.exs b/mix.exs
index b61bde168b60acf81dfa8f80cdc453416d50977c..b012efd7e36539398945660a7822164bb6fdd7fb 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -125,7 +125,7 @@ defmodule Pleroma.Mixfile do
       {:cors_plug, "~> 1.5"},
       {:ex_doc, "~> 0.20.2", only: :dev, runtime: false},
       {:web_push_encryption, "~> 0.2.1"},
-      {:swoosh, "~> 0.20"},
+      {:swoosh, "~> 0.23.2"},
       {:phoenix_swoosh, "~> 0.2"},
       {:gen_smtp, "~> 0.13"},
       {:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test},
index addd72e4bf6498400206e54ec55ca844cbb376b6..654972dddf6e8972fc743900b45b0638065e5bd2 100644 (file)
--- a/mix.lock
+++ b/mix.lock
@@ -18,7 +18,7 @@
   "crontab": {:hex, :crontab, "1.1.5", "2c9439506ceb0e9045de75879e994b88d6f0be88bfe017d58cb356c66c4a5482", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
   "crypt": {:git, "https://github.com/msantos/crypt", "1f2b58927ab57e72910191a7ebaeff984382a1d3", [ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"]},
   "db_connection": {:hex, :db_connection, "2.0.6", "bde2f85d047969c5b5800cb8f4b3ed6316c8cb11487afedac4aa5f93fd39abfa", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
-  "decimal": {:hex, :decimal, "1.7.0", "30d6b52c88541f9a66637359ddf85016df9eb266170d53105f02e4a67e00c5aa", [:mix], [], "hexpm"},
+  "decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"},
   "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm"},
   "earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm"},
   "ecto": {:hex, :ecto, "3.1.4", "69d852da7a9f04ede725855a35ede48d158ca11a404fe94f8b2fb3b2162cd3c9", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
   "ex_syslogger": {:git, "https://github.com/slashmili/ex_syslogger.git", "f3963399047af17e038897c69e20d552e6899e1d", [tag: "1.4.0"]},
   "excoveralls": {:hex, :excoveralls, "0.11.1", "dd677fbdd49114fdbdbf445540ec735808250d56b011077798316505064edb2c", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
   "floki": {:hex, :floki, "0.20.4", "be42ac911fece24b4c72f3b5846774b6e61b83fe685c2fc9d62093277fb3bc86", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}, {:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
-  "gen_smtp": {:hex, :gen_smtp, "0.13.0", "11f08504c4bdd831dc520b8f84a1dce5ce624474a797394e7aafd3c29f5dcd25", [:rebar3], [], "hexpm"},
-  "gen_stage": {:hex, :gen_stage, "0.14.1", "9d46723fda072d4f4bb31a102560013f7960f5d80ea44dcb96fd6304ed61e7a4", [:mix], [], "hexpm"},
-  "gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
-  "gettext": {:hex, :gettext, "0.16.1", "e2130b25eebcbe02bb343b119a07ae2c7e28bd4b146c4a154da2ffb2b3507af2", [:mix], [], "hexpm"},
+  "gen_smtp": {:hex, :gen_smtp, "0.14.0", "39846a03522456077c6429b4badfd1d55e5e7d0fdfb65e935b7c5e38549d9202", [:rebar3], [], "hexpm"},
+  "gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [:mix], [], "hexpm"},
   "hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
   "html_entities": {:hex, :html_entities, "0.4.0", "f2fee876858cf6aaa9db608820a3209e45a087c5177332799592142b50e89a6b", [:mix], [], "hexpm"},
   "html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
   "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
   "plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
   "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
+  "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm"},
   "postgrex": {:hex, :postgrex, "0.14.3", "5754dee2fdf6e9e508cbf49ab138df964278700b764177e8f3871e658b345a1e", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
   "prometheus": {:hex, :prometheus, "4.2.2", "a830e77b79dc6d28183f4db050a7cac926a6c58f1872f9ef94a35cd989aceef8", [:mix, :rebar3], [], "hexpm"},
   "prometheus_ecto": {:hex, :prometheus_ecto, "1.4.1", "6c768ea9654de871e5b32fab2eac348467b3021604ebebbcbd8bcbe806a65ed5", [:mix], [{:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm"},
   "prometheus_ex": {:hex, :prometheus_ex, "3.0.5", "fa58cfd983487fc5ead331e9a3e0aa622c67232b3ec71710ced122c4c453a02f", [:mix], [{:prometheus, "~> 4.0", [hex: :prometheus, repo: "hexpm", optional: false]}], "hexpm"},
   "prometheus_phoenix": {:hex, :prometheus_phoenix, "1.2.1", "964a74dfbc055f781d3a75631e06ce3816a2913976d1df7830283aa3118a797a", [:mix], [{:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.3 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm"},
   "prometheus_plugs": {:hex, :prometheus_plugs, "1.1.5", "25933d48f8af3a5941dd7b621c889749894d8a1082a6ff7c67cc99dec26377c5", [:mix], [{:accept, "~> 0.1", [hex: :accept, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}, {:prometheus_process_collector, "~> 1.1", [hex: :prometheus_process_collector, repo: "hexpm", optional: true]}], "hexpm"},
+  "prometheus_process_collector": {:hex, :prometheus_process_collector, "1.4.0", "6dbd39e3165b9ef1c94a7a820e9ffe08479f949dcdd431ed4aaea7b250eebfde", [:rebar3], [{:prometheus, "~> 4.0", [hex: :prometheus, repo: "hexpm", optional: false]}], "hexpm"},
   "quack": {:hex, :quack, "0.1.1", "cca7b4da1a233757fdb44b3334fce80c94785b3ad5a602053b7a002b5a8967bf", [:mix], [{:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm"},
   "quantum": {:hex, :quantum, "2.3.4", "72a0e8855e2adc101459eac8454787cb74ab4169de6ca50f670e72142d4960e9", [:mix], [{:calendar, "~> 0.17", [hex: :calendar, repo: "hexpm", optional: true]}, {:crontab, "~> 1.1", [hex: :crontab, repo: "hexpm", optional: false]}, {:gen_stage, "~> 0.12", [hex: :gen_stage, repo: "hexpm", optional: false]}, {:swarm, "~> 3.3", [hex: :swarm, repo: "hexpm", optional: false]}, {:timex, "~> 3.1", [hex: :timex, repo: "hexpm", optional: true]}], "hexpm"},
   "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"},
-  "swarm": {:hex, :swarm, "3.4.0", "64f8b30055d74640d2186c66354b33b999438692a91be275bb89cdc7e401f448", [:mix], [{:gen_state_machine, "~> 2.0", [hex: :gen_state_machine, repo: "hexpm", optional: false]}, {:libring, "~> 1.0", [hex: :libring, repo: "hexpm", optional: false]}], "hexpm"},
-  "swoosh": {:hex, :swoosh, "0.23.1", "209b7cc6d862c09d2a064c16caa4d4d1c9c936285476459e16591e0065f8432b", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.12", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {: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"},
+  "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"},
index 2211c98e31c3327634703f9373b20128fa78ff52..25a2f73e4ded1120617886cf79edae693c2410f2 100644 (file)
@@ -91,3 +91,375 @@ msgstr ""
 
 msgid "must be equal to %{number}"
 msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:381
+msgid "Account not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:153
+msgid "Already voted"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:263
+msgid "Bad request"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:254
+msgid "Can't delete object"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:569
+msgid "Can't delete this post"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1731
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1737
+msgid "Can't display this activity"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:195
+msgid "Can't find user"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1148
+msgid "Can't get favorites"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:263
+msgid "Can't like object"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:518
+msgid "Cannot post an empty status without attachments"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:461
+msgid "Comment must be up to %{max_size} characters"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/config.ex:63
+msgid "Config with params %{params} not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:78
+msgid "Could not delete"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:110
+msgid "Could not favorite"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:310
+msgid "Could not pin"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:89
+msgid "Could not repeat"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:120
+msgid "Could not unfavorite"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:327
+msgid "Could not unpin"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:99
+msgid "Could not unrepeat"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:392
+msgid "Could not update state"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1271
+msgid "Error."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/kocaptcha.ex:36
+msgid "Invalid CAPTCHA"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1700
+#: lib/pleroma/web/oauth/oauth_controller.ex:465
+msgid "Invalid credentials"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/ensure_authenticated_plug.ex:20
+msgid "Invalid credentials."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:154
+msgid "Invalid indices"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:411
+msgid "Invalid parameters"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:377
+msgid "Invalid password."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:163
+msgid "Invalid request"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/kocaptcha.ex:16
+msgid "Kocaptcha service unavailable"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1696
+msgid "Missing parameters"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:496
+msgid "No such conversation"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:163
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:206
+msgid "No such permission_group"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:69
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:311
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:399
+#: lib/pleroma/web/mastodon_api/subscription_controller.ex:63
+#: lib/pleroma/web/ostatus/ostatus_controller.ex:248
+msgid "Not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:152
+msgid "Poll's author can't vote"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:443
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:444
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:473
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:476
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1180
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1564
+msgid "Record not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:417
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1570
+#: lib/pleroma/web/mastodon_api/subscription_controller.ex:69
+#: lib/pleroma/web/ostatus/ostatus_controller.ex:252
+msgid "Something went wrong"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:253
+msgid "The message visibility must be direct"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:521
+msgid "The status is over the character limit"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:27
+msgid "This resource requires authentication."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/rate_limiter.ex:89
+msgid "Throttled"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:155
+msgid "Too many choices"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:268
+msgid "Unhandled activity type"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/user_is_admin_plug.ex:20
+msgid "User is not admin."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:380
+msgid "Valid `account_id` required"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:185
+msgid "You can't revoke your own admin status."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:216
+msgid "Your account is currently disabled"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:158
+#: lib/pleroma/web/oauth/oauth_controller.ex:213
+msgid "Your login is missing a confirmed e-mail address"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:221
+msgid "can't read inbox of %{nickname} as %{as_nickname}"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:297
+msgid "can't update outbox of %{nickname} as %{as_nickname}"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:335
+msgid "conversation is already muted"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:192
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:317
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1196
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1247
+msgid "error"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:789
+msgid "mascots can only be images"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:34
+msgid "not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:298
+msgid "Bad OAuth request."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:92
+msgid "CAPTCHA already used"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:89
+msgid "CAPTCHA expired"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:50
+msgid "Failed"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:314
+msgid "Failed to authenticate: %{message}."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:345
+msgid "Failed to set up user account."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/oauth_scopes_plug.ex:37
+msgid "Insufficient permissions: %{permissions}."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:89
+msgid "Internal Error"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/fallback_controller.ex:22
+#: lib/pleroma/web/oauth/fallback_controller.ex:29
+msgid "Invalid Username/Password"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:107
+msgid "Invalid answer data"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:204
+msgid "Nodeinfo schema version not handled"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:145
+msgid "This action is outside the authorized scopes"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/fallback_controller.ex:14
+msgid "Unknown error, please check the details and try again."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:93
+#: lib/pleroma/web/oauth/oauth_controller.ex:131
+msgid "Unlisted redirect_uri."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:294
+msgid "Unsupported OAuth provider: %{provider}."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/uploaders/uploader.ex:71
+msgid "Uploader callback timeout"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/uploader_controller.ex:11
+#: lib/pleroma/web/uploader_controller.ex:23
+msgid "bad request"
+msgstr ""
index a964f84ec63c7610577c1a78b3ea39705af981c0..2fd9c42e327849110b9a0ac108270069b8704f72 100644 (file)
@@ -7,7 +7,6 @@
 ## Run `mix gettext.extract` to bring this file up to
 ## date. Leave `msgstr`s empty as changing them here as no
 ## effect: edit them in PO (`.po`) files instead.
-
 ## From Ecto.Changeset.cast/4
 msgid "can't be blank"
 msgstr ""
@@ -89,3 +88,375 @@ msgstr ""
 
 msgid "must be equal to %{number}"
 msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:381
+msgid "Account not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:153
+msgid "Already voted"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:263
+msgid "Bad request"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:254
+msgid "Can't delete object"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:569
+msgid "Can't delete this post"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1731
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1737
+msgid "Can't display this activity"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:195
+msgid "Can't find user"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1148
+msgid "Can't get favorites"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:263
+msgid "Can't like object"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:518
+msgid "Cannot post an empty status without attachments"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:461
+msgid "Comment must be up to %{max_size} characters"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/config.ex:63
+msgid "Config with params %{params} not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:78
+msgid "Could not delete"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:110
+msgid "Could not favorite"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:310
+msgid "Could not pin"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:89
+msgid "Could not repeat"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:120
+msgid "Could not unfavorite"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:327
+msgid "Could not unpin"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:99
+msgid "Could not unrepeat"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:392
+msgid "Could not update state"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1271
+msgid "Error."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/kocaptcha.ex:36
+msgid "Invalid CAPTCHA"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1700
+#: lib/pleroma/web/oauth/oauth_controller.ex:465
+msgid "Invalid credentials"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/ensure_authenticated_plug.ex:20
+msgid "Invalid credentials."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:154
+msgid "Invalid indices"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:411
+msgid "Invalid parameters"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:377
+msgid "Invalid password."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:163
+msgid "Invalid request"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/kocaptcha.ex:16
+msgid "Kocaptcha service unavailable"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1696
+msgid "Missing parameters"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:496
+msgid "No such conversation"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:163
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:206
+msgid "No such permission_group"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:69
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:311
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:399
+#: lib/pleroma/web/mastodon_api/subscription_controller.ex:63
+#: lib/pleroma/web/ostatus/ostatus_controller.ex:248
+msgid "Not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:152
+msgid "Poll's author can't vote"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:443
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:444
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:473
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:476
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1180
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1564
+msgid "Record not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:417
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1570
+#: lib/pleroma/web/mastodon_api/subscription_controller.ex:69
+#: lib/pleroma/web/ostatus/ostatus_controller.ex:252
+msgid "Something went wrong"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:253
+msgid "The message visibility must be direct"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:521
+msgid "The status is over the character limit"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:27
+msgid "This resource requires authentication."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/rate_limiter.ex:89
+msgid "Throttled"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:155
+msgid "Too many choices"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:268
+msgid "Unhandled activity type"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/user_is_admin_plug.ex:20
+msgid "User is not admin."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:380
+msgid "Valid `account_id` required"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:185
+msgid "You can't revoke your own admin status."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:216
+msgid "Your account is currently disabled"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:158
+#: lib/pleroma/web/oauth/oauth_controller.ex:213
+msgid "Your login is missing a confirmed e-mail address"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:221
+msgid "can't read inbox of %{nickname} as %{as_nickname}"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:297
+msgid "can't update outbox of %{nickname} as %{as_nickname}"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:335
+msgid "conversation is already muted"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:192
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:317
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1196
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1247
+msgid "error"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:789
+msgid "mascots can only be images"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:34
+msgid "not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:298
+msgid "Bad OAuth request."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:92
+msgid "CAPTCHA already used"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:89
+msgid "CAPTCHA expired"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:50
+msgid "Failed"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:314
+msgid "Failed to authenticate: %{message}."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:345
+msgid "Failed to set up user account."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/oauth_scopes_plug.ex:37
+msgid "Insufficient permissions: %{permissions}."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:89
+msgid "Internal Error"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/fallback_controller.ex:22
+#: lib/pleroma/web/oauth/fallback_controller.ex:29
+msgid "Invalid Username/Password"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:107
+msgid "Invalid answer data"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:204
+msgid "Nodeinfo schema version not handled"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:145
+msgid "This action is outside the authorized scopes"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/fallback_controller.ex:14
+msgid "Unknown error, please check the details and try again."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:93
+#: lib/pleroma/web/oauth/oauth_controller.ex:131
+msgid "Unlisted redirect_uri."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:294
+msgid "Unsupported OAuth provider: %{provider}."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/uploaders/uploader.ex:71
+msgid "Uploader callback timeout"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/uploader_controller.ex:11
+#: lib/pleroma/web/uploader_controller.ex:23
+msgid "bad request"
+msgstr ""
diff --git a/priv/gettext/ru/LC_MESSAGES/errors.po b/priv/gettext/ru/LC_MESSAGES/errors.po
new file mode 100644 (file)
index 0000000..39f83e8
--- /dev/null
@@ -0,0 +1,463 @@
+## `msgid`s in this file come from POT (.pot) files.
+##
+## Do not add, change, or remove `msgid`s manually here as
+## they're tied to the ones in the corresponding POT file
+## (with the same domain).
+##
+## Use `mix gettext.extract --merge` or `mix gettext.merge`
+## to merge POT files into PO files.
+msgid ""
+msgstr ""
+"Language: ru\n"
+"Plural-Forms: nplurals=3\n"
+
+msgid "can't be blank"
+msgstr "не может быть пустым"
+
+msgid "has already been taken"
+msgstr "уже занято"
+
+msgid "is invalid"
+msgstr "неверный"
+
+msgid "has invalid format"
+msgstr "неверный формат"
+
+msgid "has an invalid entry"
+msgstr "содержит неверную запись"
+
+msgid "is reserved"
+msgstr "занято"
+
+msgid "does not match confirmation"
+msgstr "не совпадает"
+
+msgid "is still associated with this entry"
+msgstr "по прежнему связан с этой записью"
+
+msgid "are still associated with this entry"
+msgstr "по прежнему связаны с этой записью"
+
+msgid "should be %{count} character(s)"
+msgid_plural "should be %{count} character(s)"
+msgstr[0] "должен состоять из %{count} символа"
+msgstr[1] "должен состоять из %{count} символов"
+msgstr[2] "должен состоять из %{count} символов"
+
+
+msgid "should have %{count} item(s)"
+msgid_plural "should have %{count} item(s)"
+msgstr[0] "должен содержать %{count} элемент"
+msgstr[1] "должен содержать %{count} элемента"
+msgstr[2] "должен содержать %{count} элементов"
+
+msgid "should be at least %{count} character(s)"
+msgid_plural "should be at least %{count} character(s)"
+msgstr[0] "должен быть не менее чем %{count} символа"
+msgstr[1] "должен быть не менее чем %{count} символов"
+msgstr[2] "должен быть не менее чем %{count} символов"
+
+msgid "should have at least %{count} item(s)"
+msgid_plural "should have at least %{count} item(s)"
+msgstr[0] "должен быть не менее %{count} элемента"
+msgstr[1] "должен быть не менее %{count} элементов"
+msgstr[2] "должен быть не менее %{count} элементов"
+
+msgid "should be at most %{count} character(s)"
+msgid_plural "should be at most %{count} character(s)"
+msgstr[0] "должен быть не более %{count} символа"
+msgstr[1] "должен быть не более %{count} символов"
+msgstr[2] "должен быть не более %{count} символов"
+
+msgid "should have at most %{count} item(s)"
+msgid_plural "should have at most %{count} item(s)"
+msgstr[0] "должен содержать не менее %{count} элемента"
+msgstr[1] "должен содержать не менее %{count} элемента"
+msgstr[2] "должен содержать не менее %{count} элементов"
+
+msgid "must be less than %{number}"
+msgstr "должен быть меньше %{number}"
+
+msgid "must be greater than %{number}"
+msgstr "должен быть больше %{number}"
+
+msgid "must be less than or equal to %{number}"
+msgstr "должен быть меньше или равен %{number}"
+
+msgid "must be greater than or equal to %{number}"
+msgstr "должен быть больше или равен %{number}"
+
+msgid "must be equal to %{number}"
+msgstr "должен быть равным %{number}"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:381
+msgid "Account not found"
+msgstr "Учетная запись не найдена"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:153
+msgid "Already voted"
+msgstr "Уже проголосовал(а)"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:263
+msgid "Bad request"
+msgstr "Неверный запрос"
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:254
+msgid "Can't delete object"
+msgstr "Произошла ошибка при удалении объекта"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:569
+msgid "Can't delete this post"
+msgstr "Произошла ошибка при удалении этой записи"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1731
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1737
+msgid "Can't display this activity"
+msgstr "Произошла ошибка при показе этой записи"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:195
+msgid "Can't find user"
+msgstr "Пользователь не найден"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1148
+msgid "Can't get favorites"
+msgstr "Не в состоянии получить избранное"
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:263
+msgid "Can't like object"
+msgstr "Не могу поставить лайк"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:518
+msgid "Cannot post an empty status without attachments"
+msgstr "Нельзя отправить пустой статус без приложений"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:461
+msgid "Comment must be up to %{max_size} characters"
+msgstr "Комментарий должен быть не более %{max_size} символов"
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/config.ex:63
+msgid "Config with params %{params} not found"
+msgstr "Параметры конфигурации %{params} не найдены"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:78
+msgid "Could not delete"
+msgstr "Не в силах удалить"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:110
+msgid "Could not favorite"
+msgstr "Не в силах добавить в избранное"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:310
+msgid "Could not pin"
+msgstr "Не в силах прикрепить"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:89
+msgid "Could not repeat"
+msgstr "Не в силах повторить"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:120
+msgid "Could not unfavorite"
+msgstr "Не в силах удалить из избранного"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:327
+msgid "Could not unpin"
+msgstr "Не в силах открепить"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:99
+msgid "Could not unrepeat"
+msgstr "Не в силах отменить повтор"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:392
+msgid "Could not update state"
+msgstr "Не в силах обновить состояние"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1271
+msgid "Error."
+msgstr "Ошибка"
+
+#, elixir-format
+#: lib/pleroma/captcha/kocaptcha.ex:36
+msgid "Invalid CAPTCHA"
+msgstr "Неверная CAPTCHA"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1700
+#: lib/pleroma/web/oauth/oauth_controller.ex:465
+msgid "Invalid credentials"
+msgstr "Неверные учетные данные"
+
+#, elixir-format
+#: lib/pleroma/plugs/ensure_authenticated_plug.ex:20
+msgid "Invalid credentials."
+msgstr "Неверные учетные данные"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:154
+msgid "Invalid indices"
+msgstr "Неверные индексы"
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:411
+msgid "Invalid parameters"
+msgstr "Неверны параметры"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:377
+msgid "Invalid password."
+msgstr "Неверный пароль"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:163
+msgid "Invalid request"
+msgstr "Неверный запрос"
+
+#, elixir-format
+#: lib/pleroma/captcha/kocaptcha.ex:16
+msgid "Kocaptcha service unavailable"
+msgstr "Kocaptcha недоступен"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1696
+msgid "Missing parameters"
+msgstr "Не хватает параметров"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:496
+msgid "No such conversation"
+msgstr "Разговор не найден"
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:163
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:206
+msgid "No such permission_group"
+msgstr "Такой группы полномочий не существует"
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:69
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:311
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:399
+#: lib/pleroma/web/mastodon_api/subscription_controller.ex:63
+#: lib/pleroma/web/ostatus/ostatus_controller.ex:248
+msgid "Not found"
+msgstr "Не найден"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:152
+msgid "Poll's author can't vote"
+msgstr "Автор опроса не может голосовать"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:443
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:444
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:473
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:476
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1180
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1564
+msgid "Record not found"
+msgstr "Запись не найдена"
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:417
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1570
+#: lib/pleroma/web/mastodon_api/subscription_controller.ex:69
+#: lib/pleroma/web/ostatus/ostatus_controller.ex:252
+msgid "Something went wrong"
+msgstr "Что-то пошло не так"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:253
+msgid "The message visibility must be direct"
+msgstr "Видимость у сообщения должна быть `Личное`"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:521
+msgid "The status is over the character limit"
+msgstr "Превышена длина статуса"
+
+#, elixir-format
+#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:27
+msgid "This resource requires authentication."
+msgstr "Для этого ресурса требуется аутентификация"
+
+#, elixir-format
+#: lib/pleroma/plugs/rate_limiter.ex:89
+msgid "Throttled"
+msgstr "Ограничено. Превышен лимит запросов."
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:155
+msgid "Too many choices"
+msgstr "Слишком много ответов"
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:268
+msgid "Unhandled activity type"
+msgstr "Неизвестный тип activity"
+
+#, elixir-format
+#: lib/pleroma/plugs/user_is_admin_plug.ex:20
+msgid "User is not admin."
+msgstr "Пользователь не обладает правами администратора"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:380
+msgid "Valid `account_id` required"
+msgstr "Требуется корректный `account_id`"
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:185
+msgid "You can't revoke your own admin status."
+msgstr "Вы не можете отозвать статус администратора у вашей учетной записи"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:216
+msgid "Your account is currently disabled"
+msgstr "Ваша учетная запись отключена"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:158
+#: lib/pleroma/web/oauth/oauth_controller.ex:213
+msgid "Your login is missing a confirmed e-mail address"
+msgstr "Ваш e-mail адрес не подтвержден"
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:221
+msgid "can't read inbox of %{nickname} as %{as_nickname}"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:297
+msgid "can't update outbox of %{nickname} as %{as_nickname}"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:335
+msgid "conversation is already muted"
+msgstr "разговор уже игнорируется"
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:192
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:317
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1196
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1247
+msgid "error"
+msgstr "ошибка"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:789
+msgid "mascots can only be images"
+msgstr "маскоты должны быть картинками"
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:34
+msgid "not found"
+msgstr "не найдено"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:298
+msgid "Bad OAuth request."
+msgstr "Неверный OAuth запрос"
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:92
+msgid "CAPTCHA already used"
+msgstr "CAPTCHA уже использована"
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:89
+msgid "CAPTCHA expired"
+msgstr "CAPTCHA устарела"
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:50
+msgid "Failed"
+msgstr "Ошибка"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:314
+msgid "Failed to authenticate: %{message}."
+msgstr "Ошибка при входе: %{message}"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:345
+msgid "Failed to set up user account."
+msgstr "Ошибка при создании учетной записи"
+
+#, elixir-format
+#: lib/pleroma/plugs/oauth_scopes_plug.ex:37
+msgid "Insufficient permissions: %{permissions}."
+msgstr "Недостаточно полномочий: %{permissions}"
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:89
+msgid "Internal Error"
+msgstr "Внутренняя ошибка"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/fallback_controller.ex:22
+#: lib/pleroma/web/oauth/fallback_controller.ex:29
+msgid "Invalid Username/Password"
+msgstr "Неверное имя пользователя или пароль"
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:107
+msgid "Invalid answer data"
+msgstr "Неверный ответ"
+
+#, elixir-format
+#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:204
+msgid "Nodeinfo schema version not handled"
+msgstr "Версия схемы Nodeinfo не учитывается"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:145
+msgid "This action is outside the authorized scopes"
+msgstr "Это действие выходит за рамки доступных полномочий"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/fallback_controller.ex:14
+msgid "Unknown error, please check the details and try again."
+msgstr "Неизвестная ошибка. Пожалуйста, проверьте данные и попробуйте снова."
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:93
+#: lib/pleroma/web/oauth/oauth_controller.ex:131
+msgid "Unlisted redirect_uri."
+msgstr "Неизвестный redirect_uri"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:294
+msgid "Unsupported OAuth provider: %{provider}."
+msgstr "Неизвестный OAuth провайдер: %{provider}"
+
+#, elixir-format
+#: lib/pleroma/uploaders/uploader.ex:71
+msgid "Uploader callback timeout"
+msgstr "Тайм-аут при загрузке"
+
+#, elixir-format
+#: lib/pleroma/web/uploader_controller.ex:11
+#: lib/pleroma/web/uploader_controller.ex:23
+msgid "bad request"
+msgstr "неправильный запрос"
diff --git a/priv/repo/migrations/20190710115833_add_following_address_to_user.exs b/priv/repo/migrations/20190710115833_add_following_address_to_user.exs
new file mode 100644 (file)
index 0000000..fe30472
--- /dev/null
@@ -0,0 +1,9 @@
+defmodule Pleroma.Repo.Migrations.AddFollowingAddressToUser do
+  use Ecto.Migration
+
+  def change do
+    alter table(:users) do
+      add(:following_address, :string, unique: true)
+    end
+  end
+end
diff --git a/priv/repo/migrations/20190710125051_add_following_address_index_to_user.exs b/priv/repo/migrations/20190710125051_add_following_address_index_to_user.exs
new file mode 100644 (file)
index 0000000..0cbfb71
--- /dev/null
@@ -0,0 +1,8 @@
+defmodule Pleroma.Repo.Migrations.AddFollowingAddressIndexToUser do
+  use Ecto.Migration
+
+  @disable_ddl_transaction true
+  def change do
+    create(index(:users, [:following_address], concurrently: true))
+  end
+end
diff --git a/priv/repo/migrations/20190710125158_add_following_address_from_source_data.exs b/priv/repo/migrations/20190710125158_add_following_address_from_source_data.exs
new file mode 100644 (file)
index 0000000..779aa38
--- /dev/null
@@ -0,0 +1,20 @@
+defmodule Pleroma.Repo.Migrations.AddFollowingAddressFromSourceData do
+  use Ecto.Migration
+  import Ecto.Query
+  alias Pleroma.User
+
+  def change do
+    query =
+      User.external_users_query()
+      |> select([u], struct(u, [:id, :ap_id, :info]))
+
+    Pleroma.Repo.stream(query)
+    |> Enum.each(fn
+      %{info: %{source_data: source_data}} = user ->
+        Ecto.Changeset.cast(user, %{following_address: source_data["following"]}, [
+          :following_address
+        ])
+        |> Pleroma.Repo.update()
+    end)
+  end
+end
index 5b4dad648aa8a1d08c7ec89c51c134939587b04b..dc75d4008e0ff631d54d9408dfbe952283c15cb6 100644 (file)
@@ -11,6 +11,7 @@ end %>
 
 config :pleroma, Pleroma.Web.Endpoint,
    url: [host: "<%= domain %>", scheme: "https", port: <%= port %>],
+   http: [ip: {<%= String.replace(listen_ip, ".", ", ") %>}, port: <%= listen_port %>],
    secret_key_base: "<%= secret %>",
    signing_salt: "<%= signing_salt %>"
 
index 7ba4363c863320ea2bb2f7fffb1a0a178d4f58be..b27f6fd369ac82f20f07ee6f84445eafdf12cf1d 100644 (file)
@@ -6,6 +6,7 @@ defmodule Pleroma.ActivityTest do
   use Pleroma.DataCase
   alias Pleroma.Activity
   alias Pleroma.Bookmark
+  alias Pleroma.Object
   alias Pleroma.ThreadMute
   import Pleroma.Factory
 
@@ -18,15 +19,18 @@ defmodule Pleroma.ActivityTest do
 
   test "returns activities by it's objects AP ids" do
     activity = insert(:note_activity)
-    [found_activity] = Activity.get_all_create_by_object_ap_id(activity.data["object"]["id"])
+    object_data = Object.normalize(activity).data
+
+    [found_activity] = Activity.get_all_create_by_object_ap_id(object_data["id"])
 
     assert activity == found_activity
   end
 
   test "returns the activity that created an object" do
     activity = insert(:note_activity)
+    object_data = Object.normalize(activity).data
 
-    found_activity = Activity.get_create_by_object_ap_id(activity.data["object"]["id"])
+    found_activity = Activity.get_create_by_object_ap_id(object_data["id"])
 
     assert activity == found_activity
   end
index 7d5d68d1132a3d5f2ab1f55f3da46b45dfff96b7..4f0c13417fda7274692517eefa1fed307a0a1f34 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.BBS.HandlerTest do
   use Pleroma.DataCase
   alias Pleroma.Activity
@@ -59,6 +63,7 @@ defmodule Pleroma.BBS.HandlerTest do
     another_user = insert(:user)
 
     {:ok, activity} = CommonAPI.post(another_user, %{"status" => "this is a test post"})
+    activity_object = Object.normalize(activity)
 
     output =
       capture_io(fn ->
@@ -76,8 +81,9 @@ defmodule Pleroma.BBS.HandlerTest do
       )
 
     assert reply.actor == user.ap_id
-    object = Object.normalize(reply)
-    assert object.data["content"] == "this is a reply"
-    assert object.data["inReplyTo"] == activity.data["object"]
+
+    reply_object_data = Object.normalize(reply).data
+    assert reply_object_data["content"] == "this is a reply"
+    assert reply_object_data["inReplyTo"] == activity_object.data["id"]
   end
 end
index b81c102eff87084d9ede55b2c12ddf9944bdd340..e54bd359c7de54d5b1fbe8b59e446e3004e77513 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.BookmarkTest do
   use Pleroma.DataCase
   import Pleroma.Factory
index c0e433263577f5a914c76970a7eefb83192d9284..dbeadbe877ba02e4ebc0a604c1d70da62d8a528a 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Config.TransferTaskTest do
   use Pleroma.DataCase
 
diff --git a/test/emails/admin_email_test.exs b/test/emails/admin_email_test.exs
new file mode 100644 (file)
index 0000000..4bf54b0
--- /dev/null
@@ -0,0 +1,37 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Emails.AdminEmailTest do
+  use Pleroma.DataCase
+  import Pleroma.Factory
+
+  alias Pleroma.Emails.AdminEmail
+  alias Pleroma.Web.Router.Helpers
+
+  test "build report email" do
+    config = Pleroma.Config.get(:instance)
+    to_user = insert(:user)
+    reporter = insert(:user)
+    account = insert(:user)
+
+    res =
+      AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment")
+
+    status_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, "12")
+    reporter_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.nickname)
+    account_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, account.nickname)
+
+    assert res.to == [{to_user.name, to_user.email}]
+    assert res.from == {config[:name], config[:notify_email]}
+    assert res.reply_to == {reporter.name, reporter.email}
+    assert res.subject == "#{config[:name]} Report"
+
+    assert res.html_body ==
+             "<p>Reported by: <a href=\"#{reporter_url}\">#{reporter.nickname}</a></p>\n<p>Reported Account: <a href=\"#{
+               account_url
+             }\">#{account.nickname}</a></p>\n<p>Comment: Test comment\n<p> Statuses:\n  <ul>\n    <li><a href=\"#{
+               status_url
+             }\">#{status_url}</li>\n  </ul>\n</p>\n\n"
+  end
+end
diff --git a/test/emails/mailer_test.exs b/test/emails/mailer_test.exs
new file mode 100644 (file)
index 0000000..450bb09
--- /dev/null
@@ -0,0 +1,57 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Emails.MailerTest do
+  use Pleroma.DataCase
+  alias Pleroma.Emails.Mailer
+
+  import Swoosh.TestAssertions
+
+  @email %Swoosh.Email{
+    from: {"Pleroma", "noreply@example.com"},
+    html_body: "Test email",
+    subject: "Pleroma test email",
+    to: [{"Test User", "user1@example.com"}]
+  }
+
+  setup do
+    value = Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled])
+    on_exit(fn -> Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], value) end)
+    :ok
+  end
+
+  test "not send email when mailer is disabled" do
+    Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false)
+    Mailer.deliver(@email)
+
+    refute_email_sent(
+      from: {"Pleroma", "noreply@example.com"},
+      to: [{"Test User", "user1@example.com"}],
+      html_body: "Test email",
+      subject: "Pleroma test email"
+    )
+  end
+
+  test "send email" do
+    Mailer.deliver(@email)
+
+    assert_email_sent(
+      from: {"Pleroma", "noreply@example.com"},
+      to: [{"Test User", "user1@example.com"}],
+      html_body: "Test email",
+      subject: "Pleroma test email"
+    )
+  end
+
+  test "perform" do
+    Mailer.perform(:deliver_async, @email, [])
+
+    assert_email_sent(
+      from: {"Pleroma", "noreply@example.com"},
+      to: [{"Test User", "user1@example.com"}],
+      html_body: "Test email",
+      subject: "Pleroma test email"
+    )
+  end
+end
diff --git a/test/emails/user_email_test.exs b/test/emails/user_email_test.exs
new file mode 100644 (file)
index 0000000..7d8df6a
--- /dev/null
@@ -0,0 +1,48 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Emails.UserEmailTest do
+  use Pleroma.DataCase
+
+  alias Pleroma.Emails.UserEmail
+  alias Pleroma.Web.Endpoint
+  alias Pleroma.Web.Router
+
+  import Pleroma.Factory
+
+  test "build password reset email" do
+    config = Pleroma.Config.get(:instance)
+    user = insert(:user)
+    email = UserEmail.password_reset_email(user, "test_token")
+    assert email.from == {config[:name], config[:notify_email]}
+    assert email.to == [{user.name, user.email}]
+    assert email.subject == "Password reset"
+    assert email.html_body =~ Router.Helpers.reset_password_url(Endpoint, :reset, "test_token")
+  end
+
+  test "build user invitation email" do
+    config = Pleroma.Config.get(:instance)
+    user = insert(:user)
+    token = %Pleroma.UserInviteToken{token: "test-token"}
+    email = UserEmail.user_invitation_email(user, token, "test@test.com", "Jonh")
+    assert email.from == {config[:name], config[:notify_email]}
+    assert email.subject == "Invitation to Pleroma"
+    assert email.to == [{"Jonh", "test@test.com"}]
+
+    assert email.html_body =~
+             Router.Helpers.redirect_url(Endpoint, :registration_page, token.token)
+  end
+
+  test "build account confirmation email" do
+    config = Pleroma.Config.get(:instance)
+    user = insert(:user, info: %Pleroma.User.Info{confirmation_token: "conf-token"})
+    email = UserEmail.account_confirmation_email(user)
+    assert email.from == {config[:name], config[:notify_email]}
+    assert email.to == [{user.name, user.email}]
+    assert email.subject == "#{config[:name]} account confirmation"
+
+    assert email.html_body =~
+             Router.Helpers.confirm_email_url(Endpoint, :confirm_email, user.id, "conf-token")
+  end
+end
index 2eaa26be695af36aa0d92d4e4207ebb2c68f7d05..07ac6ff1daada3d8da311f2c512fa59c967c736e 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.EmojiTest do
   use ExUnit.Case, async: true
   alias Pleroma.Emoji
diff --git a/test/fixtures/host-meta-zetsubou.xn--q9jyb4c.xml b/test/fixtures/host-meta-zetsubou.xn--q9jyb4c.xml
new file mode 100644 (file)
index 0000000..df64d44
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<XRD
+  xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
+  <Link rel="lrdd" template="https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource={uri}" type="application/xrd+xml" />
+</XRD>
diff --git a/test/fixtures/lain.xml b/test/fixtures/lain.xml
new file mode 100644 (file)
index 0000000..332b3b2
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<XRD
+  xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
+  <Subject>acct:lain@zetsubou.xn--q9jyb4c</Subject>
+  <Alias>https://zetsubou.xn--q9jyb4c/users/lain</Alias>
+  <Link href="https://zetsubou.xn--q9jyb4c/users/lain/feed.atom" rel="http://schemas.google.com/g/2010#updates-from" type="application/atom+xml" />
+  <Link href="https://zetsubou.xn--q9jyb4c/users/lain" rel="http://webfinger.net/rel/profile-page" type="text/html" />
+  <Link href="https://zetsubou.xn--q9jyb4c/users/lain/salmon" rel="salmon" />
+  <Link href="data:application/magic-public-key,RSA.7yTJNuPH7wSsg6sMH4XLi-OL6JL8idyRMwNsWy2xzKWPJRWVK5hxG1kMGQ4qC_9ksqIaT7c7DIQFJYYbhRTnXYdac1UxaWivzl5l2HYPOOF1_-gbE6TCaI4ItTQo5eB4yyy3zozrIuv_GY8W0Ww58Re8Z_G4DFFmnipgiBKNaHthxNQqtxcK-o4rUv3xdyr_M9KYi3QISCGiaV_t8xkdVREixzNmWpsqM5YZ46xXT0SiGSHDubLE_OGhyvWqf_WkJrnDBETL3WjXU4QsPmBbVBgLvLcHei_uAD-9d3QImSuWwBXXQZIzY7Diro6u8dZuPIoLmnbUp1-mViBwCUMWSQ==.AQAB" rel="magic-public-key" />
+  <Link href="https://zetsubou.xn--q9jyb4c/users/lain" rel="self" type="application/activity+json" />
+  <Link rel="http://ostatus.org/schema/1.0/subscribe" template="https://zetsubou.xn--q9jyb4c/ostatus_subscribe?acct={uri}" />
+</XRD>
diff --git a/test/fixtures/mastodon-delete-user.json b/test/fixtures/mastodon-delete-user.json
new file mode 100644 (file)
index 0000000..f19088f
--- /dev/null
@@ -0,0 +1,24 @@
+{
+  "type": "Delete",
+  "object": {
+    "type": "Person",
+    "id": "http://mastodon.example.org/users/admin",
+    "atomUri": "http://mastodon.example.org/users/admin"
+  },
+  "id": "http://mastodon.example.org/users/admin#delete",
+  "actor": "http://mastodon.example.org/users/admin",
+  "@context": [
+    {
+      "toot": "http://joinmastodon.org/ns#",
+      "sensitive": "as:sensitive",
+      "ostatus": "http://ostatus.org#",
+      "movedTo": "as:movedTo",
+      "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+      "inReplyToAtomUri": "ostatus:inReplyToAtomUri",
+      "conversation": "ostatus:conversation",
+      "atomUri": "ostatus:atomUri",
+      "Hashtag": "as:Hashtag",
+      "Emoji": "toot:Emoji"
+    }
+  ]
+}
index e050612200c2ad320bccd80ae9a554e90fe70889..6bb8d5b7fff026a91155b596f784181093d44304 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.HealthcheckTest do
   use Pleroma.DataCase
   alias Pleroma.Healthcheck
index a368999fff6e967f0612f86cfd456ec019b39d23..7febe84c50e06b6076f874a3ace09532337e7b7e 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.HTTP.RequestBuilderTest do
   use ExUnit.Case, async: true
   alias Pleroma.HTTP.RequestBuilder
index 776fdea6f3158dd830a78fdbd79d9cd40e05fdde..059f70b74ddecb5110778e5f2e244e8f7edff858 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.KeysTest do
   use Pleroma.DataCase
 
index a860355b8e21eff9a64d8f8a8f1692f56f8daf33..1beed623646936ce43fd3bee76203ee119f2b640 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Object.ContainmentTest do
   use Pleroma.DataCase
 
index 26dc9496deb5c609617d518c789912eaf9604a8d..3b666e0d141fc77a8604442c1087b7622041a598 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Object.FetcherTest do
   use Pleroma.DataCase
 
index b8d6aff89ce87ebc17342f50a8663ed0d3f72070..f8251b5c78ff09c47c1148fd6eeb6ed30da439bb 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Plugs.RateLimiterTest do
   use ExUnit.Case, async: true
   use Plug.Test
diff --git a/test/plugs/set_locale_plug_test.exs b/test/plugs/set_locale_plug_test.exs
new file mode 100644 (file)
index 0000000..b6c4c1c
--- /dev/null
@@ -0,0 +1,46 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Plugs.SetLocalePlugTest do
+  use ExUnit.Case, async: true
+  use Plug.Test
+
+  alias Pleroma.Plugs.SetLocalePlug
+  alias Plug.Conn
+
+  test "default locale is `en`" do
+    conn =
+      :get
+      |> conn("/cofe")
+      |> SetLocalePlug.call([])
+
+    assert "en" == Gettext.get_locale()
+    assert %{locale: "en"} == conn.assigns
+  end
+
+  test "use supported locale from `accept-language`" do
+    conn =
+      :get
+      |> conn("/cofe")
+      |> Conn.put_req_header(
+        "accept-language",
+        "ru, fr-CH, fr;q=0.9, en;q=0.8, *;q=0.5"
+      )
+      |> SetLocalePlug.call([])
+
+    assert "ru" == Gettext.get_locale()
+    assert %{locale: "ru"} == conn.assigns
+  end
+
+  test "use default locale if locale from `accept-language` is not supported" do
+    conn =
+      :get
+      |> conn("/cofe")
+      |> Conn.put_req_header("accept-language", "tlh")
+      |> SetLocalePlug.call([])
+
+    assert "en" == Gettext.get_locale()
+    assert %{locale: "en"} == conn.assigns
+  end
+end
index 85085a1fa0c2ab7473a9b830dfcdcef9fb350c30..85b64d4d152d6e684df9aae5d4d98564e6afccd8 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.RepoTest do
   use Pleroma.DataCase
   import Pleroma.Factory
index 75a61445a50bfa95019ed9347dbc5ee7fb3aa7ed..f542de97ce3f0f130b0314eaa750dbe99adcf9d1 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.ReverseProxyTest do
   use Pleroma.Web.ConnCase, async: true
   import ExUnit.CaptureLog
index dd57a0bed183ea1a51526955c3b7d2b2db60e1ab..c6a72521ad232a4654249a1417144ce3083afef0 100644 (file)
@@ -4,6 +4,7 @@
 
 defmodule Pleroma.Factory do
   use ExMachina.Ecto, repo: Pleroma.Repo
+  alias Pleroma.Object
   alias Pleroma.User
 
   def participation_factory do
@@ -38,6 +39,7 @@ defmodule Pleroma.Factory do
       user
       | ap_id: User.ap_id(user),
         follower_address: User.ap_followers(user),
+        following_address: User.ap_following(user),
         following: [User.ap_id(user)]
     }
   end
@@ -117,13 +119,14 @@ defmodule Pleroma.Factory do
   def note_activity_factory(attrs \\ %{}) do
     user = attrs[:user] || insert(:user)
     note = attrs[:note] || insert(:note, user: user)
+    attrs = Map.drop(attrs, [:user, :note])
 
     data = %{
       "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
       "type" => "Create",
       "actor" => note.data["actor"],
       "to" => note.data["to"],
-      "object" => note.data,
+      "object" => note.data["id"],
       "published" => DateTime.utc_now() |> DateTime.to_iso8601(),
       "context" => note.data["context"]
     }
@@ -133,6 +136,7 @@ defmodule Pleroma.Factory do
       actor: data["actor"],
       recipients: data["to"]
     }
+    |> Map.merge(attrs)
   end
 
   def article_activity_factory do
@@ -177,13 +181,14 @@ defmodule Pleroma.Factory do
 
   def like_activity_factory do
     note_activity = insert(:note_activity)
+    object = Object.normalize(note_activity)
     user = insert(:user)
 
     data = %{
       "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
       "actor" => user.ap_id,
       "type" => "Like",
-      "object" => note_activity.data["object"]["id"],
+      "object" => object.data["id"],
       "published_at" => DateTime.utc_now() |> DateTime.to_iso8601()
     }
 
index c593a5e4aff92aa94f8bd2a116554e1a94e3ad9c..ff6bb78f9f8c0e8919eeb438242a37af5aabe01a 100644 (file)
@@ -840,6 +840,45 @@ defmodule HttpRequestMock do
      }}
   end
 
+  def get(
+        "https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource=lain@zetsubou.xn--q9jyb4c",
+        _,
+        _,
+        Accept: "application/xrd+xml,application/jrd+json"
+      ) do
+    {:ok,
+     %Tesla.Env{
+       status: 200,
+       body: File.read!("test/fixtures/lain.xml")
+     }}
+  end
+
+  def get(
+        "https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource=https://zetsubou.xn--q9jyb4c/users/lain",
+        _,
+        _,
+        Accept: "application/xrd+xml,application/jrd+json"
+      ) do
+    {:ok,
+     %Tesla.Env{
+       status: 200,
+       body: File.read!("test/fixtures/lain.xml")
+     }}
+  end
+
+  def get(
+        "https://zetsubou.xn--q9jyb4c/.well-known/host-meta",
+        _,
+        _,
+        _
+      ) do
+    {:ok,
+     %Tesla.Env{
+       status: 200,
+       body: File.read!("test/fixtures/host-meta-zetsubou.xn--q9jyb4c.xml")
+     }}
+  end
+
   def get(url, query, body, headers) do
     {:error,
      "Not implemented the mock response for get #{inspect(url)}, #{query}, #{inspect(body)}, #{
index 83a363356c302c922b03969d0ab794a823946f1e..bbcc5721719a7de635ad95130d8f7d53df49ea3b 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Mix.Tasks.Pleroma.ConfigTest do
   use Pleroma.DataCase
   alias Pleroma.Repo
index b48662c88b9c6eb5caacfb9cda6d1f98cc9f61b9..a1b9ca1740ef6213870b4cb972cbbf9123ac38d1 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Mix.Tasks.Pleroma.EctoTest do
   use ExUnit.Case, async: true
 
index 33d093fca6cf03f7ed6449e0626f73bc543fba27..c33c4e9400c4bff33542b42a15e941547c6947a7 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Mix.Tasks.Pleroma.Ecto.RollbackTest do
   use Pleroma.DataCase
   import ExUnit.CaptureLog
similarity index 78%
rename from test/tasks/instance.exs
rename to test/tasks/instance_test.exs
index 1875f52a357dc4cd4673e4069915a47dc24f65d7..70986374eda7b611da94c40f5ba6c2eed7690fc3 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.InstanceTest do
   use ExUnit.Case, async: true
 
@@ -38,7 +42,17 @@ defmodule Pleroma.InstanceTest do
         "--indexable",
         "y",
         "--db-configurable",
-        "y"
+        "y",
+        "--rum",
+        "y",
+        "--listen-port",
+        "4000",
+        "--listen-ip",
+        "127.0.0.1",
+        "--uploads-dir",
+        "test/uploads",
+        "--static-dir",
+        "instance/static/"
       ])
     end
 
@@ -56,10 +70,11 @@ defmodule Pleroma.InstanceTest do
     assert generated_config =~ "username: \"dbuser\""
     assert generated_config =~ "password: \"dbpass\""
     assert generated_config =~ "dynamic_configuration: true"
+    assert generated_config =~ "http: [ip: {127, 0, 0, 1}, port: 4000]"
     assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql()
   end
 
   defp generated_setup_psql do
-    ~s(CREATE USER dbuser WITH ENCRYPTED PASSWORD 'dbpass';\nCREATE DATABASE dbname OWNER dbuser;\n\\c dbname;\n--Extensions made by ecto.migrate that need superuser access\nCREATE EXTENSION IF NOT EXISTS citext;\nCREATE EXTENSION IF NOT EXISTS pg_trgm;\nCREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";\n)
+    ~s(CREATE USER dbuser WITH ENCRYPTED PASSWORD 'dbpass';\nCREATE DATABASE dbname OWNER dbuser;\n\\c dbname;\n--Extensions made by ecto.migrate that need superuser access\nCREATE EXTENSION IF NOT EXISTS citext;\nCREATE EXTENSION IF NOT EXISTS pg_trgm;\nCREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";\nCREATE EXTENSION IF NOT EXISTS rum;\n)
   end
 end
index e236ccbbb8701a2e23074f28f10720faa9bca768..a20bd9cf294cc5b1ffd41566293c4a3d8096b98d 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Mix.PleromaTest do
   use ExUnit.Case, async: true
   import Mix.Pleroma
index 539193f7397b4554f8b1f728df489945d216c3f8..78a3f17b44f93d6c768ccfe9b4634d235694660f 100644 (file)
@@ -1,5 +1,9 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Mix.Tasks.Pleroma.RobotsTxtTest do
-  use ExUnit.Case, async: true
+  use ExUnit.Case
   alias Mix.Tasks.Pleroma.RobotsTxt
 
   test "creates new dir" do
index 02241cfa41148357ff0699538b13b611a4fddb45..a31b38ab1208de3acee3c1ed421dee71846a0c88 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Upload.Filter.AnonymizeFilenameTest do
   use Pleroma.DataCase
 
diff --git a/test/user/synchronization_test.exs b/test/user/synchronization_test.exs
deleted file mode 100644 (file)
index 67b6694..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.User.SynchronizationTest do
-  use Pleroma.DataCase
-  import Pleroma.Factory
-  alias Pleroma.User
-  alias Pleroma.User.Synchronization
-
-  setup do
-    Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
-    :ok
-  end
-
-  test "update following/followers counters" do
-    user1 =
-      insert(:user,
-        local: false,
-        ap_id: "http://localhost:4001/users/masto_closed"
-      )
-
-    user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
-
-    users = User.external_users()
-    assert length(users) == 2
-    {user, %{}} = Synchronization.call(users, %{})
-    assert user == List.last(users)
-
-    %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
-    assert followers == 437
-    assert following == 152
-
-    %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
-
-    assert followers == 527
-    assert following == 267
-  end
-
-  test "don't check host if errors exist" do
-    user1 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser1")
-
-    user2 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser2")
-
-    users = User.external_users()
-    assert length(users) == 2
-
-    {user, %{"domain-with-errors" => 2}} =
-      Synchronization.call(users, %{"domain-with-errors" => 2}, max_retries: 2)
-
-    assert user == List.last(users)
-
-    %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
-    assert followers == 0
-    assert following == 0
-
-    %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
-
-    assert followers == 0
-    assert following == 0
-  end
-
-  test "don't check host if errors appeared" do
-    user1 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser1")
-
-    user2 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser2")
-
-    users = User.external_users()
-    assert length(users) == 2
-
-    {user, %{"domain-with-errors" => 2}} = Synchronization.call(users, %{}, max_retries: 2)
-
-    assert user == List.last(users)
-
-    %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
-    assert followers == 0
-    assert following == 0
-
-    %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
-
-    assert followers == 0
-    assert following == 0
-  end
-
-  test "other users after error appeared" do
-    user1 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser1")
-    user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
-
-    users = User.external_users()
-    assert length(users) == 2
-
-    {user, %{"domain-with-errors" => 2}} = Synchronization.call(users, %{}, max_retries: 2)
-    assert user == List.last(users)
-
-    %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
-    assert followers == 0
-    assert following == 0
-
-    %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
-
-    assert followers == 527
-    assert following == 267
-  end
-end
diff --git a/test/user/synchronization_worker_test.exs b/test/user/synchronization_worker_test.exs
deleted file mode 100644 (file)
index 835c532..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.User.SynchronizationWorkerTest do
-  use Pleroma.DataCase
-  import Pleroma.Factory
-
-  setup do
-    Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
-
-    config = Pleroma.Config.get([:instance, :external_user_synchronization])
-
-    for_update = [enabled: true, interval: 1000]
-
-    Pleroma.Config.put([:instance, :external_user_synchronization], for_update)
-
-    on_exit(fn ->
-      Pleroma.Config.put([:instance, :external_user_synchronization], config)
-    end)
-
-    :ok
-  end
-
-  test "sync follow counters" do
-    user1 =
-      insert(:user,
-        local: false,
-        ap_id: "http://localhost:4001/users/masto_closed"
-      )
-
-    user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
-
-    {:ok, _} = Pleroma.User.SynchronizationWorker.start_link()
-    :timer.sleep(1500)
-
-    %{follower_count: followers, following_count: following} =
-      Pleroma.User.get_cached_user_info(user1)
-
-    assert followers == 437
-    assert following == 152
-
-    %{follower_count: followers, following_count: following} =
-      Pleroma.User.get_cached_user_info(user2)
-
-    assert followers == 527
-    assert following == 267
-  end
-end
index 276788254a761b54c89a9399bfdeff6fc688d09d..111e40361c5581913d3ee77bb2cdaf03fb1c80bf 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.UserInviteTokenTest do
   use ExUnit.Case, async: true
   use Pleroma.DataCase
index 3c13d8b0f018a884227d5da18aadb9ae9b3cacd1..48ce973ad255f073eb9bd97817387e20b8559544 100644 (file)
@@ -255,5 +255,57 @@ defmodule Pleroma.UserSearchTest do
       [result] = User.search("lain@localhost", resolve: true, for_user: user)
       assert Map.put(result, :search_rank, nil) |> Map.put(:search_type, nil) == local_user
     end
+
+    test "works with idna domains" do
+      user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな")))
+
+      results = User.search("lain@zetsubou.みんな", resolve: false, for_user: user)
+
+      result = List.first(results)
+
+      assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
+    end
+
+    test "works with idna domains converted input" do
+      user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな")))
+
+      results =
+        User.search("lain@zetsubou." <> to_string(:idna.encode("zetsubou.みんな")),
+          resolve: false,
+          for_user: user
+        )
+
+      result = List.first(results)
+
+      assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
+    end
+
+    test "works with idna domains and bad chars in domain" do
+      user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな")))
+
+      results =
+        User.search("lain@zetsubou!@#$%^&*()+,-/:;<=>?[]'_{}|~`.みんな",
+          resolve: false,
+          for_user: user
+        )
+
+      result = List.first(results)
+
+      assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
+    end
+
+    test "works with idna domains and query as link" do
+      user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな")))
+
+      results =
+        User.search("https://zetsubou.みんな/users/lain",
+          resolve: false,
+          for_user: user
+        )
+
+      result = List.first(results)
+
+      assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
+    end
   end
 end
index 6dec3959ce7addeae35e29f2e837784760a8dd64..658c0a9f24903e063f20fea532b4cdc2395247f5 100644 (file)
@@ -14,6 +14,7 @@ defmodule Pleroma.UserTest do
   use Pleroma.DataCase
 
   import Pleroma.Factory
+  import Mock
 
   setup_all do
     Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
@@ -53,6 +54,14 @@ defmodule Pleroma.UserTest do
     assert expected_followers_collection == User.ap_followers(user)
   end
 
+  test "ap_following returns the following collection for the user" do
+    user = UserBuilder.build()
+
+    expected_followers_collection = "#{User.ap_id(user)}/following"
+
+    assert expected_followers_collection == User.ap_following(user)
+  end
+
   test "returns all pending follow requests" do
     unlocked = insert(:user)
     locked = insert(:user, %{info: %{locked: true}})
@@ -915,49 +924,80 @@ defmodule Pleroma.UserTest do
     end
   end
 
-  test ".delete_user_activities deletes all create activities" do
-    user = insert(:user)
+  describe "delete" do
+    setup do
+      {:ok, user} = insert(:user) |> User.set_cache()
 
-    {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
+      [user: user]
+    end
 
-    {:ok, _} = User.delete_user_activities(user)
+    test ".delete_user_activities deletes all create activities", %{user: user} do
+      {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
 
-    # TODO: Remove favorites, repeats, delete activities.
-    refute Activity.get_by_id(activity.id)
-  end
+      {:ok, _} = User.delete_user_activities(user)
 
-  test ".delete deactivates a user, all follow relationships and all activities" do
-    user = insert(:user)
-    follower = insert(:user)
+      # TODO: Remove favorites, repeats, delete activities.
+      refute Activity.get_by_id(activity.id)
+    end
 
-    {:ok, follower} = User.follow(follower, user)
+    test "it deletes a user, all follow relationships and all activities", %{user: user} do
+      follower = insert(:user)
+      {:ok, follower} = User.follow(follower, user)
+
+      object = insert(:note, user: user)
+      activity = insert(:note_activity, user: user, note: object)
 
-    {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
-    {:ok, activity_two} = CommonAPI.post(follower, %{"status" => "3hu"})
+      object_two = insert(:note, user: follower)
+      activity_two = insert(:note_activity, user: follower, note: object_two)
 
-    {:ok, like, _} = CommonAPI.favorite(activity_two.id, user)
-    {:ok, like_two, _} = CommonAPI.favorite(activity.id, follower)
-    {:ok, repeat, _} = CommonAPI.repeat(activity_two.id, user)
+      {:ok, like, _} = CommonAPI.favorite(activity_two.id, user)
+      {:ok, like_two, _} = CommonAPI.favorite(activity.id, follower)
+      {:ok, repeat, _} = CommonAPI.repeat(activity_two.id, user)
 
-    {:ok, _} = User.delete(user)
+      {:ok, _} = User.delete(user)
 
-    follower = User.get_cached_by_id(follower.id)
+      follower = User.get_cached_by_id(follower.id)
+
+      refute User.following?(follower, user)
+      refute User.get_by_id(user.id)
+      assert {:ok, nil} == Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
+
+      user_activities =
+        user.ap_id
+        |> Activity.query_by_actor()
+        |> Repo.all()
+        |> Enum.map(fn act -> act.data["type"] end)
+
+      assert Enum.all?(user_activities, fn act -> act in ~w(Delete Undo) end)
+
+      refute Activity.get_by_id(activity.id)
+      refute Activity.get_by_id(like.id)
+      refute Activity.get_by_id(like_two.id)
+      refute Activity.get_by_id(repeat.id)
+    end
 
-    refute User.following?(follower, user)
-    refute User.get_by_id(user.id)
+    test_with_mock "it sends out User Delete activity",
+                   %{user: user},
+                   Pleroma.Web.ActivityPub.Publisher,
+                   [:passthrough],
+                   [] do
+      config_path = [:instance, :federating]
+      initial_setting = Pleroma.Config.get(config_path)
+      Pleroma.Config.put(config_path, true)
 
-    user_activities =
-      user.ap_id
-      |> Activity.query_by_actor()
-      |> Repo.all()
-      |> Enum.map(fn act -> act.data["type"] end)
+      {:ok, follower} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin")
+      {:ok, _} = User.follow(follower, user)
 
-    assert Enum.all?(user_activities, fn act -> act in ~w(Delete Undo) end)
+      {:ok, _user} = User.delete(user)
 
-    refute Activity.get_by_id(activity.id)
-    refute Activity.get_by_id(like.id)
-    refute Activity.get_by_id(like_two.id)
-    refute Activity.get_by_id(repeat.id)
+      assert called(
+               Pleroma.Web.ActivityPub.Publisher.publish_one(%{
+                 inbox: "http://mastodon.example.org/inbox"
+               })
+             )
+
+      Pleroma.Config.put(config_path, initial_setting)
+    end
   end
 
   test "get_public_key_for_ap_id fetches a user that's not in the db" do
@@ -1311,52 +1351,6 @@ defmodule Pleroma.UserTest do
 
       assert User.external_users(max_id: fdb_user2.id, limit: 1) == []
     end
-
-    test "sync_follow_counters/1", %{user1: user1, user2: user2} do
-      {:ok, _pid} = Agent.start_link(fn -> %{} end, name: :domain_errors)
-
-      :ok = User.sync_follow_counters()
-
-      %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
-      assert followers == 437
-      assert following == 152
-
-      %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
-
-      assert followers == 527
-      assert following == 267
-
-      Agent.stop(:domain_errors)
-    end
-
-    test "sync_follow_counters/1 in separate batches", %{user1: user1, user2: user2} do
-      {:ok, _pid} = Agent.start_link(fn -> %{} end, name: :domain_errors)
-
-      :ok = User.sync_follow_counters(limit: 1)
-
-      %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
-      assert followers == 437
-      assert following == 152
-
-      %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
-
-      assert followers == 527
-      assert following == 267
-
-      Agent.stop(:domain_errors)
-    end
-
-    test "perform/1 with :sync_follow_counters", %{user1: user1, user2: user2} do
-      :ok = User.perform(:sync_follow_counters)
-      %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
-      assert followers == 437
-      assert following == 152
-
-      %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
-
-      assert followers == 527
-      assert following == 267
-    end
   end
 
   describe "set_info_cache/2" do
index 5a8a671552f18cf9bc218e4da47791e613ae49f3..1f8eb9d7161466be86793b91b12c5fddd4de11e9 100644 (file)
@@ -170,7 +170,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
   describe "/object/:uuid/likes" do
     test "it returns the like activities in a collection", %{conn: conn} do
       like = insert(:like_activity)
-      uuid = String.split(like.data["object"], "/") |> List.last()
+      like_object_ap_id = Object.normalize(like).data["id"]
+      uuid = String.split(like_object_ap_id, "/") |> List.last()
 
       result =
         conn
@@ -309,6 +310,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
 
     test "it returns a note activity in a collection", %{conn: conn} do
       note_activity = insert(:direct_note_activity)
+      note_object = Object.normalize(note_activity)
       user = User.get_cached_by_ap_id(hd(note_activity.data["to"]))
 
       conn =
@@ -317,7 +319,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
         |> put_req_header("accept", "application/activity+json")
         |> get("/users/#{user.nickname}/inbox")
 
-      assert response(conn, 200) =~ note_activity.data["object"]["content"]
+      assert response(conn, 200) =~ note_object.data["content"]
     end
 
     test "it clears `unreachable` federation status of the sender", %{conn: conn, data: data} do
@@ -395,6 +397,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
 
     test "it returns a note activity in a collection", %{conn: conn} do
       note_activity = insert(:note_activity)
+      note_object = Object.normalize(note_activity)
       user = User.get_cached_by_ap_id(note_activity.data["actor"])
 
       conn =
@@ -402,7 +405,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
         |> put_req_header("accept", "application/activity+json")
         |> get("/users/#{user.nickname}/outbox")
 
-      assert response(conn, 200) =~ note_activity.data["object"]["content"]
+      assert response(conn, 200) =~ note_object.data["content"]
     end
 
     test "it returns an announce activity in a collection", %{conn: conn} do
@@ -464,12 +467,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
 
     test "it erects a tombstone when receiving a delete activity", %{conn: conn} do
       note_activity = insert(:note_activity)
+      note_object = Object.normalize(note_activity)
       user = User.get_cached_by_ap_id(note_activity.data["actor"])
 
       data = %{
         type: "Delete",
         object: %{
-          id: note_activity.data["object"]["id"]
+          id: note_object.data["id"]
         }
       }
 
@@ -482,19 +486,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
       result = json_response(conn, 201)
       assert Activity.get_by_ap_id(result["id"])
 
-      object = Object.get_by_ap_id(note_activity.data["object"]["id"])
-      assert object
+      assert object = Object.get_by_ap_id(note_object.data["id"])
       assert object.data["type"] == "Tombstone"
     end
 
     test "it rejects delete activity of object from other actor", %{conn: conn} do
       note_activity = insert(:note_activity)
+      note_object = Object.normalize(note_activity)
       user = insert(:user)
 
       data = %{
         type: "Delete",
         object: %{
-          id: note_activity.data["object"]["id"]
+          id: note_object.data["id"]
         }
       }
 
@@ -509,12 +513,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
 
     test "it increases like count when receiving a like action", %{conn: conn} do
       note_activity = insert(:note_activity)
+      note_object = Object.normalize(note_activity)
       user = User.get_cached_by_ap_id(note_activity.data["actor"])
 
       data = %{
         type: "Like",
         object: %{
-          id: note_activity.data["object"]["id"]
+          id: note_object.data["id"]
         }
       }
 
@@ -527,8 +532,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
       result = json_response(conn, 201)
       assert Activity.get_by_ap_id(result["id"])
 
-      object = Object.get_by_ap_id(note_activity.data["object"]["id"])
-      assert object
+      assert object = Object.get_by_ap_id(note_object.data["id"])
       assert object.data["like_count"] == 1
     end
   end
index 76586ee4ada3977ea2d6d5bb665568d7377f8814..59d56f3a758324633b0d70953ebd076f5117f3ca 100644 (file)
@@ -254,10 +254,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
       }
 
       {:ok, %Activity{} = activity} = ActivityPub.insert(data)
-      object = Object.normalize(activity.data["object"])
-
+      assert object = Object.normalize(activity)
       assert is_binary(object.data["id"])
-      assert %Object{} = Object.get_by_ap_id(activity.data["object"])
     end
   end
 
@@ -659,7 +657,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
   describe "like an object" do
     test "adds a like activity to the db" do
       note_activity = insert(:note_activity)
-      object = Object.get_by_ap_id(note_activity.data["object"]["id"])
+      assert object = Object.normalize(note_activity)
+
       user = insert(:user)
       user_two = insert(:user)
 
@@ -678,19 +677,23 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
 
       assert like_activity == same_like_activity
       assert object.data["likes"] == [user.ap_id]
+      assert object.data["like_count"] == 1
 
       [note_activity] = Activity.get_all_create_by_object_ap_id(object.data["id"])
       assert note_activity.data["object"]["like_count"] == 1
 
       {:ok, _like_activity, object} = ActivityPub.like(user_two, object)
       assert object.data["like_count"] == 2
+
+      [note_activity] = Activity.get_all_create_by_object_ap_id(object.data["id"])
+      assert note_activity.data["object"]["like_count"] == 2
     end
   end
 
   describe "unliking" do
     test "unliking a previously liked object" do
       note_activity = insert(:note_activity)
-      object = Object.get_by_ap_id(note_activity.data["object"]["id"])
+      object = Object.normalize(note_activity)
       user = insert(:user)
 
       # Unliking something that hasn't been liked does nothing
@@ -710,7 +713,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
   describe "announcing an object" do
     test "adds an announce activity to the db" do
       note_activity = insert(:note_activity)
-      object = Object.get_by_ap_id(note_activity.data["object"]["id"])
+      object = Object.normalize(note_activity)
       user = insert(:user)
 
       {:ok, announce_activity, object} = ActivityPub.announce(user, object)
@@ -731,7 +734,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
   describe "unannouncing an object" do
     test "unannouncing a previously announced object" do
       note_activity = insert(:note_activity)
-      object = Object.get_by_ap_id(note_activity.data["object"]["id"])
+      object = Object.normalize(note_activity)
       user = insert(:user)
 
       # Unannouncing an object that is not announced does nothing
@@ -810,10 +813,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
       assert activity.data["type"] == "Undo"
       assert activity.data["actor"] == follower.ap_id
 
-      assert is_map(activity.data["object"])
-      assert activity.data["object"]["type"] == "Follow"
-      assert activity.data["object"]["object"] == followed.ap_id
-      assert activity.data["object"]["id"] == follow_activity.data["id"]
+      embedded_object = activity.data["object"]
+      assert is_map(embedded_object)
+      assert embedded_object["type"] == "Follow"
+      assert embedded_object["object"] == followed.ap_id
+      assert embedded_object["id"] == follow_activity.data["id"]
     end
   end
 
@@ -839,22 +843,23 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
       assert activity.data["type"] == "Undo"
       assert activity.data["actor"] == blocker.ap_id
 
-      assert is_map(activity.data["object"])
-      assert activity.data["object"]["type"] == "Block"
-      assert activity.data["object"]["object"] == blocked.ap_id
-      assert activity.data["object"]["id"] == block_activity.data["id"]
+      embedded_object = activity.data["object"]
+      assert is_map(embedded_object)
+      assert embedded_object["type"] == "Block"
+      assert embedded_object["object"] == blocked.ap_id
+      assert embedded_object["id"] == block_activity.data["id"]
     end
   end
 
   describe "deletion" do
     test "it creates a delete activity and deletes the original object" do
       note = insert(:note_activity)
-      object = Object.get_by_ap_id(note.data["object"]["id"])
+      object = Object.normalize(note)
       {:ok, delete} = ActivityPub.delete(object)
 
       assert delete.data["type"] == "Delete"
       assert delete.data["actor"] == note.data["actor"]
-      assert delete.data["object"] == note.data["object"]["id"]
+      assert delete.data["object"] == object.data["id"]
 
       assert Activity.get_by_id(delete.id) != nil
 
@@ -900,13 +905,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
     test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
       user = insert(:user)
       note = insert(:note_activity)
+      object = Object.normalize(note)
 
       {:ok, object} =
-        Object.get_by_ap_id(note.data["object"]["id"])
+        object
         |> Object.change(%{
           data: %{
-            "actor" => note.data["object"]["actor"],
-            "id" => note.data["object"]["id"],
+            "actor" => object.data["actor"],
+            "id" => object.data["id"],
             "to" => [user.ap_id],
             "type" => "Note"
           }
@@ -1018,8 +1024,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
 
       assert update.data["actor"] == user.ap_id
       assert update.data["to"] == [user.follower_address]
-      assert update.data["object"]["id"] == user_data["id"]
-      assert update.data["object"]["type"] == user_data["type"]
+      assert embedded_object = update.data["object"]
+      assert embedded_object["id"] == user_data["id"]
+      assert embedded_object["type"] == user_data["type"]
     end
   end
 
diff --git a/test/web/activity_pub/mrf/ensure_re_prepended_test.exs b/test/web/activity_pub/mrf/ensure_re_prepended_test.exs
new file mode 100644 (file)
index 0000000..dbc8b9e
--- /dev/null
@@ -0,0 +1,82 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrependedTest do
+  use Pleroma.DataCase
+
+  alias Pleroma.Activity
+  alias Pleroma.Object
+  alias Pleroma.Web.ActivityPub.MRF.EnsureRePrepended
+
+  describe "rewrites summary" do
+    test "it adds `re:` to summary object when child summary and parent summary equal" do
+      message = %{
+        "type" => "Create",
+        "object" => %{
+          "summary" => "object-summary",
+          "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "object-summary"}}}
+        }
+      }
+
+      assert {:ok, res} = EnsureRePrepended.filter(message)
+      assert res["object"]["summary"] == "re: object-summary"
+    end
+
+    test "it adds `re:` to summary object when child summary containts re-subject of parent summary " do
+      message = %{
+        "type" => "Create",
+        "object" => %{
+          "summary" => "object-summary",
+          "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "re: object-summary"}}}
+        }
+      }
+
+      assert {:ok, res} = EnsureRePrepended.filter(message)
+      assert res["object"]["summary"] == "re: object-summary"
+    end
+  end
+
+  describe "skip filter" do
+    test "it skip if type isn't 'Create'" do
+      message = %{
+        "type" => "Annotation",
+        "object" => %{"summary" => "object-summary"}
+      }
+
+      assert {:ok, res} = EnsureRePrepended.filter(message)
+      assert res == message
+    end
+
+    test "it skip if summary is empty" do
+      message = %{
+        "type" => "Create",
+        "object" => %{
+          "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "summary"}}}
+        }
+      }
+
+      assert {:ok, res} = EnsureRePrepended.filter(message)
+      assert res == message
+    end
+
+    test "it skip if inReplyTo is empty" do
+      message = %{"type" => "Create", "object" => %{"summary" => "summary"}}
+      assert {:ok, res} = EnsureRePrepended.filter(message)
+      assert res == message
+    end
+
+    test "it skip if parent and child summary isn't equal" do
+      message = %{
+        "type" => "Create",
+        "object" => %{
+          "summary" => "object-summary",
+          "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "summary"}}}
+        }
+      }
+
+      assert {:ok, res} = EnsureRePrepended.filter(message)
+      assert res == message
+    end
+  end
+end
diff --git a/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs b/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs
new file mode 100644 (file)
index 0000000..63ed711
--- /dev/null
@@ -0,0 +1,37 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicyTest do
+  use Pleroma.DataCase
+  alias Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy
+
+  test "it clears content object" do
+    message = %{
+      "type" => "Create",
+      "object" => %{"content" => ".", "attachment" => "image"}
+    }
+
+    assert {:ok, res} = NoPlaceholderTextPolicy.filter(message)
+    assert res["object"]["content"] == ""
+
+    message = put_in(message, ["object", "content"], "<p>.</p>")
+    assert {:ok, res} = NoPlaceholderTextPolicy.filter(message)
+    assert res["object"]["content"] == ""
+  end
+
+  @messages [
+    %{
+      "type" => "Create",
+      "object" => %{"content" => "test", "attachment" => "image"}
+    },
+    %{"type" => "Create", "object" => %{"content" => "."}},
+    %{"type" => "Create", "object" => %{"content" => "<p>.</p>"}}
+  ]
+  test "it skips filter" do
+    Enum.each(@messages, fn message ->
+      assert {:ok, res} = NoPlaceholderTextPolicy.filter(message)
+      assert res == message
+    end)
+  end
+end
diff --git a/test/web/activity_pub/mrf/normalize_markup_test.exs b/test/web/activity_pub/mrf/normalize_markup_test.exs
new file mode 100644 (file)
index 0000000..3916a1f
--- /dev/null
@@ -0,0 +1,42 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkupTest do
+  use Pleroma.DataCase
+  alias Pleroma.Web.ActivityPub.MRF.NormalizeMarkup
+
+  @html_sample """
+  <b>this is in bold</b>
+  <p>this is a paragraph</p>
+  this is a linebreak<br />
+  this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a>
+  this is a link with not allowed "rel" attribute: <a href="http://example.com/" rel="tag noallowed">example.com</a>
+  this is an image: <img src="http://example.com/image.jpg"><br />
+  <script>alert('hacked')</script>
+  """
+
+  test "it filter html tags" do
+    expected = """
+    <b>this is in bold</b>
+    <p>this is a paragraph</p>
+    this is a linebreak<br />
+    this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a>
+    this is a link with not allowed "rel" attribute: <a href="http://example.com/">example.com</a>
+    this is an image: <img src="http://example.com/image.jpg" /><br />
+    alert('hacked')
+    """
+
+    message = %{"type" => "Create", "object" => %{"content" => @html_sample}}
+
+    assert {:ok, res} = NormalizeMarkup.filter(message)
+    assert res["object"]["content"] == expected
+  end
+
+  test "it skips filter if type isn't `Create`" do
+    message = %{"type" => "Note", "object" => %{}}
+
+    assert {:ok, res} = NormalizeMarkup.filter(message)
+    assert res == message
+  end
+end
diff --git a/test/web/activity_pub/mrf/reject_non_public_test.exs b/test/web/activity_pub/mrf/reject_non_public_test.exs
new file mode 100644 (file)
index 0000000..fdf6b24
--- /dev/null
@@ -0,0 +1,105 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublicTest do
+  use Pleroma.DataCase
+  import Pleroma.Factory
+
+  alias Pleroma.Web.ActivityPub.MRF.RejectNonPublic
+
+  setup do
+    policy = Pleroma.Config.get([:mrf_rejectnonpublic])
+    on_exit(fn -> Pleroma.Config.put([:mrf_rejectnonpublic], policy) end)
+
+    :ok
+  end
+
+  describe "public message" do
+    test "it's allowed when address is public" do
+      actor = insert(:user, follower_address: "test-address")
+
+      message = %{
+        "actor" => actor.ap_id,
+        "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+        "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+        "type" => "Create"
+      }
+
+      assert {:ok, message} = RejectNonPublic.filter(message)
+    end
+
+    test "it's allowed when cc address contain public address" do
+      actor = insert(:user, follower_address: "test-address")
+
+      message = %{
+        "actor" => actor.ap_id,
+        "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+        "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+        "type" => "Create"
+      }
+
+      assert {:ok, message} = RejectNonPublic.filter(message)
+    end
+  end
+
+  describe "followers message" do
+    test "it's allowed when addrer of message in the follower addresses of user and it enabled in config" do
+      actor = insert(:user, follower_address: "test-address")
+
+      message = %{
+        "actor" => actor.ap_id,
+        "to" => ["test-address"],
+        "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+        "type" => "Create"
+      }
+
+      Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], true)
+      assert {:ok, message} = RejectNonPublic.filter(message)
+    end
+
+    test "it's rejected when addrer of message in the follower addresses of user and it disabled in config" do
+      actor = insert(:user, follower_address: "test-address")
+
+      message = %{
+        "actor" => actor.ap_id,
+        "to" => ["test-address"],
+        "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+        "type" => "Create"
+      }
+
+      Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], false)
+      assert {:reject, nil} = RejectNonPublic.filter(message)
+    end
+  end
+
+  describe "direct message" do
+    test "it's allows when direct messages are allow" do
+      actor = insert(:user)
+
+      message = %{
+        "actor" => actor.ap_id,
+        "to" => ["https://www.w3.org/ns/activitystreams#Publid"],
+        "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+        "type" => "Create"
+      }
+
+      Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], true)
+      assert {:ok, message} = RejectNonPublic.filter(message)
+    end
+
+    test "it's reject when direct messages aren't allow" do
+      actor = insert(:user)
+
+      message = %{
+        "actor" => actor.ap_id,
+        "to" => ["https://www.w3.org/ns/activitystreams#Publid~~~"],
+        "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+        "type" => "Create"
+      }
+
+      Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], false)
+      assert {:reject, nil} = RejectNonPublic.filter(message)
+    end
+  end
+end
diff --git a/test/web/activity_pub/mrf/tag_policy_test.exs b/test/web/activity_pub/mrf/tag_policy_test.exs
new file mode 100644 (file)
index 0000000..4aa3531
--- /dev/null
@@ -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.TagPolicyTest do
+  use Pleroma.DataCase
+  import Pleroma.Factory
+
+  alias Pleroma.Web.ActivityPub.MRF.TagPolicy
+  @public "https://www.w3.org/ns/activitystreams#Public"
+
+  describe "mrf_tag:disable-any-subscription" do
+    test "rejects message" do
+      actor = insert(:user, tags: ["mrf_tag:disable-any-subscription"])
+      message = %{"object" => actor.ap_id, "type" => "Follow"}
+      assert {:reject, nil} = TagPolicy.filter(message)
+    end
+  end
+
+  describe "mrf_tag:disable-remote-subscription" do
+    test "rejects non-local follow requests" do
+      actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"])
+      follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: false)
+      message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id}
+      assert {:reject, nil} = TagPolicy.filter(message)
+    end
+
+    test "allows non-local follow requests" do
+      actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"])
+      follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: true)
+      message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id}
+      assert {:ok, message} = TagPolicy.filter(message)
+    end
+  end
+
+  describe "mrf_tag:sandbox" do
+    test "removes from public timelines" do
+      actor = insert(:user, tags: ["mrf_tag:sandbox"])
+
+      message = %{
+        "actor" => actor.ap_id,
+        "type" => "Create",
+        "object" => %{},
+        "to" => [@public, "f"],
+        "cc" => [@public, "d"]
+      }
+
+      except_message = %{
+        "actor" => actor.ap_id,
+        "type" => "Create",
+        "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d"]},
+        "to" => ["f", actor.follower_address],
+        "cc" => ["d"]
+      }
+
+      assert TagPolicy.filter(message) == {:ok, except_message}
+    end
+  end
+
+  describe "mrf_tag:force-unlisted" do
+    test "removes from the federated timeline" do
+      actor = insert(:user, tags: ["mrf_tag:force-unlisted"])
+
+      message = %{
+        "actor" => actor.ap_id,
+        "type" => "Create",
+        "object" => %{},
+        "to" => [@public, "f"],
+        "cc" => [actor.follower_address, "d"]
+      }
+
+      except_message = %{
+        "actor" => actor.ap_id,
+        "type" => "Create",
+        "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]},
+        "to" => ["f", actor.follower_address],
+        "cc" => ["d", @public]
+      }
+
+      assert TagPolicy.filter(message) == {:ok, except_message}
+    end
+  end
+
+  describe "mrf_tag:media-strip" do
+    test "removes attachments" do
+      actor = insert(:user, tags: ["mrf_tag:media-strip"])
+
+      message = %{
+        "actor" => actor.ap_id,
+        "type" => "Create",
+        "object" => %{"attachment" => ["file1"]}
+      }
+
+      except_message = %{
+        "actor" => actor.ap_id,
+        "type" => "Create",
+        "object" => %{}
+      }
+
+      assert TagPolicy.filter(message) == {:ok, except_message}
+    end
+  end
+
+  describe "mrf_tag:media-force-nsfw" do
+    test "Mark as sensitive on presence of attachments" do
+      actor = insert(:user, tags: ["mrf_tag:media-force-nsfw"])
+
+      message = %{
+        "actor" => actor.ap_id,
+        "type" => "Create",
+        "object" => %{"tag" => ["test"], "attachment" => ["file1"]}
+      }
+
+      except_message = %{
+        "actor" => actor.ap_id,
+        "type" => "Create",
+        "object" => %{"tag" => ["test", "nsfw"], "attachment" => ["file1"], "sensitive" => true}
+      }
+
+      assert TagPolicy.filter(message) == {:ok, except_message}
+    end
+  end
+end
diff --git a/test/web/activity_pub/mrf/user_allowlist_policy_test.exs b/test/web/activity_pub/mrf/user_allowlist_policy_test.exs
new file mode 100644 (file)
index 0000000..6519e23
--- /dev/null
@@ -0,0 +1,36 @@
+# 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.UserAllowListPolicyTest do
+  use Pleroma.DataCase
+  import Pleroma.Factory
+
+  alias Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy
+
+  setup do
+    policy = Pleroma.Config.get([:mrf_user_allowlist]) || []
+    on_exit(fn -> Pleroma.Config.put([:mrf_user_allowlist], policy) end)
+
+    :ok
+  end
+
+  test "pass filter if allow list is empty" do
+    actor = insert(:user)
+    message = %{"actor" => actor.ap_id}
+    assert UserAllowListPolicy.filter(message) == {:ok, message}
+  end
+
+  test "pass filter if allow list isn't empty and user in allow list" do
+    actor = insert(:user)
+    Pleroma.Config.put([:mrf_user_allowlist, :localhost], [actor.ap_id, "test-ap-id"])
+    message = %{"actor" => actor.ap_id}
+    assert UserAllowListPolicy.filter(message) == {:ok, message}
+  end
+
+  test "rejected if allow list isn't empty and user not in allow list" do
+    actor = insert(:user)
+    Pleroma.Config.put([:mrf_user_allowlist, :localhost], ["test-ap-id"])
+    message = %{"actor" => actor.ap_id}
+    assert UserAllowListPolicy.filter(message) == {:reject, nil}
+  end
+end
index a914d3c4c77065462384820c36d7bf01003cb2c4..b896a532b806e4d950e7fe7b378107bf2727851f 100644 (file)
@@ -31,7 +31,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       data =
         File.read!("test/fixtures/mastodon-post-activity.json")
         |> Poison.decode!()
-        |> Map.put("object", activity.data["object"])
+        |> Map.put("object", Object.normalize(activity).data)
 
       {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
 
@@ -49,8 +49,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       data = Map.put(data, "object", object)
       {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
-
-      returned_object = Object.normalize(returned_activity.data["object"], false)
+      returned_object = Object.normalize(returned_activity, false)
 
       assert activity =
                Activity.get_create_by_object_ap_id(
@@ -75,7 +74,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
         allowed_incoming_reply_depth?: fn _ -> false end do
         {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
 
-        returned_object = Object.normalize(returned_activity.data["object"], false)
+        returned_object = Object.normalize(returned_activity, false)
 
         refute Activity.get_create_by_object_ap_id(
                  "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
@@ -124,25 +123,27 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       assert data["actor"] == "http://mastodon.example.org/users/admin"
 
-      object = Object.normalize(data["object"]).data
-      assert object["id"] == "http://mastodon.example.org/users/admin/statuses/99512778738411822"
+      object_data = Object.normalize(data["object"]).data
+
+      assert object_data["id"] ==
+               "http://mastodon.example.org/users/admin/statuses/99512778738411822"
 
-      assert object["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
+      assert object_data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
 
-      assert object["cc"] == [
+      assert object_data["cc"] == [
                "http://mastodon.example.org/users/admin/followers",
                "http://localtesting.pleroma.lol/users/lain"
              ]
 
-      assert object["actor"] == "http://mastodon.example.org/users/admin"
-      assert object["attributedTo"] == "http://mastodon.example.org/users/admin"
+      assert object_data["actor"] == "http://mastodon.example.org/users/admin"
+      assert object_data["attributedTo"] == "http://mastodon.example.org/users/admin"
 
-      assert object["context"] ==
+      assert object_data["context"] ==
                "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation"
 
-      assert object["sensitive"] == true
+      assert object_data["sensitive"] == true
 
-      user = User.get_cached_by_ap_id(object["actor"])
+      user = User.get_cached_by_ap_id(object_data["actor"])
 
       assert user.info.note_count == 1
     end
@@ -552,6 +553,30 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert Activity.get_by_id(activity.id)
     end
 
+    test "it works for incoming user deletes" do
+      %{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
+
+      data =
+        File.read!("test/fixtures/mastodon-delete-user.json")
+        |> Poison.decode!()
+
+      {:ok, _} = Transmogrifier.handle_incoming(data)
+
+      refute User.get_cached_by_ap_id(ap_id)
+    end
+
+    test "it fails for incoming user deletes with spoofed origin" do
+      %{ap_id: ap_id} = insert(:user)
+
+      data =
+        File.read!("test/fixtures/mastodon-delete-user.json")
+        |> Poison.decode!()
+        |> Map.put("actor", ap_id)
+
+      assert :error == Transmogrifier.handle_incoming(data)
+      assert User.get_cached_by_ap_id(ap_id)
+    end
+
     test "it works for incoming unannounces with an existing notice" do
       user = insert(:user)
       {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
@@ -573,10 +598,11 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
 
       assert data["type"] == "Undo"
-      assert data["object"]["type"] == "Announce"
-      assert data["object"]["object"] == activity.data["object"]
+      assert object_data = data["object"]
+      assert object_data["type"] == "Announce"
+      assert object_data["object"] == activity.data["object"]
 
-      assert data["object"]["id"] ==
+      assert object_data["id"] ==
                "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
     end
 
@@ -886,7 +912,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       other_user = insert(:user)
 
       {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
-      object = Object.normalize(activity.data["object"])
+      object = Object.normalize(activity)
 
       message = %{
         "@context" => "https://www.w3.org/ns/activitystreams",
@@ -1095,6 +1121,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert user.info.ap_enabled
       assert user.info.note_count == 1
       assert user.follower_address == "https://niu.moe/users/rye/followers"
+      assert user.following_address == "https://niu.moe/users/rye/following"
 
       user = User.get_cached_by_id(user.id)
       assert user.info.note_count == 1
@@ -1332,4 +1359,32 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       refute recipient.follower_address in fixed_object["to"]
     end
   end
+
+  test "update_following_followers_counters/1" do
+    user1 =
+      insert(:user,
+        local: false,
+        follower_address: "http://localhost:4001/users/masto_closed/followers",
+        following_address: "http://localhost:4001/users/masto_closed/following"
+      )
+
+    user2 =
+      insert(:user,
+        local: false,
+        follower_address: "http://localhost:4001/users/fuser2/followers",
+        following_address: "http://localhost:4001/users/fuser2/following"
+      )
+
+    Transmogrifier.update_following_followers_counters(user1)
+    Transmogrifier.update_following_followers_counters(user2)
+
+    %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
+    assert followers == 437
+    assert following == 152
+
+    %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
+
+    assert followers == 527
+    assert following == 267
+  end
 end
index 932d5f5e78d8fae5cd0a77bc1cbf5e6f7be5deaa..ca5f057a7053540c996e77d4d1434eb335893e89 100644 (file)
@@ -1,3 +1,7 @@
+# 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.UtilsTest do
   use Pleroma.DataCase
   alias Pleroma.Activity
index d939fc5a74038cf3ee2dec70dbc549c1a740abec..13447dc297172731211ebd936186732b3eb43188 100644 (file)
@@ -1,7 +1,12 @@
+# 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.ObjectViewTest do
   use Pleroma.DataCase
   import Pleroma.Factory
 
+  alias Pleroma.Object
   alias Pleroma.Web.ActivityPub.ObjectView
   alias Pleroma.Web.CommonAPI
 
@@ -19,19 +24,21 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do
 
   test "renders a note activity" do
     note = insert(:note_activity)
+    object = Object.normalize(note)
 
     result = ObjectView.render("object.json", %{object: note})
 
     assert result["id"] == note.data["id"]
     assert result["to"] == note.data["to"]
     assert result["object"]["type"] == "Note"
-    assert result["object"]["content"] == note.data["object"]["content"]
+    assert result["object"]["content"] == object.data["content"]
     assert result["type"] == "Create"
     assert result["@context"]
   end
 
   test "renders a like activity" do
     note = insert(:note_activity)
+    object = Object.normalize(note)
     user = insert(:user)
 
     {:ok, like_activity, _} = CommonAPI.favorite(note.id, user)
@@ -39,12 +46,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do
     result = ObjectView.render("object.json", %{object: like_activity})
 
     assert result["id"] == like_activity.data["id"]
-    assert result["object"] == note.data["object"]["id"]
+    assert result["object"] == object.data["id"]
     assert result["type"] == "Like"
   end
 
   test "renders an announce activity" do
     note = insert(:note_activity)
+    object = Object.normalize(note)
     user = insert(:user)
 
     {:ok, announce_activity, _} = CommonAPI.repeat(note.id, user)
@@ -52,7 +60,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do
     result = ObjectView.render("object.json", %{object: announce_activity})
 
     assert result["id"] == announce_activity.data["id"]
-    assert result["object"] == note.data["object"]["id"]
+    assert result["object"] == object.data["id"]
     assert result["type"] == "Announce"
   end
 end
index e6483db8bdc8f9dde223425fa6f3951332ffd9d7..969860c4cec1124d0e672da3d0d0c178524d6a46 100644 (file)
@@ -1,3 +1,7 @@
+# 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.UserViewTest do
   use Pleroma.DataCase
   import Pleroma.Factory
index e24df3cab3dd8140af246ac52f3c69fc4b56003c..4d5c07da42a8d8bac425f44dfcbbb147aa8f655a 100644 (file)
@@ -1,3 +1,7 @@
+# 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.VisibilityTest do
   use Pleroma.DataCase
 
index 4ea33a6cc483cfbdf540c73019dfc1a3746e50c8..1b71cbff384835fb033489f71d90b41b452c82c8 100644 (file)
@@ -1234,7 +1234,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
       recipients = Enum.map(response["mentions"], & &1["username"])
 
-      assert conn.assigns[:user].nickname in recipients
       assert reporter.nickname in recipients
       assert response["content"] == "I will check it out"
       assert response["visibility"] == "direct"
@@ -1408,14 +1407,19 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
         post(conn, "/api/pleroma/admin/config", %{
           configs: [
             %{group: "pleroma", key: "key1", value: "value1"},
+            %{
+              group: "ueberauth",
+              key: "Ueberauth.Strategy.Twitter.OAuth",
+              value: [%{"tuple" => [":consumer_secret", "aaaa"]}]
+            },
             %{
               group: "pleroma",
               key: "key2",
               value: %{
-                "nested_1" => "nested_value1",
-                "nested_2" => [
-                  %{"nested_22" => "nested_value222"},
-                  %{"nested_33" => %{"nested_44" => "nested_444"}}
+                ":nested_1" => "nested_value1",
+                ":nested_2" => [
+                  %{":nested_22" => "nested_value222"},
+                  %{":nested_33" => %{":nested_44" => "nested_444"}}
                 ]
               }
             },
@@ -1424,13 +1428,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
               key: "key3",
               value: [
                 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
-                %{"nested_4" => ":true"}
+                %{"nested_4" => true}
               ]
             },
             %{
               group: "pleroma",
               key: "key4",
-              value: %{"nested_5" => ":upload", "endpoint" => "https://example.com"}
+              value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"}
             },
             %{
               group: "idna",
@@ -1447,31 +1451,34 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    "key" => "key1",
                    "value" => "value1"
                  },
+                 %{
+                   "group" => "ueberauth",
+                   "key" => "Ueberauth.Strategy.Twitter.OAuth",
+                   "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}]
+                 },
                  %{
                    "group" => "pleroma",
                    "key" => "key2",
-                   "value" => [
-                     %{"nested_1" => "nested_value1"},
-                     %{
-                       "nested_2" => [
-                         %{"nested_22" => "nested_value222"},
-                         %{"nested_33" => %{"nested_44" => "nested_444"}}
-                       ]
-                     }
-                   ]
+                   "value" => %{
+                     ":nested_1" => "nested_value1",
+                     ":nested_2" => [
+                       %{":nested_22" => "nested_value222"},
+                       %{":nested_33" => %{":nested_44" => "nested_444"}}
+                     ]
+                   }
                  },
                  %{
                    "group" => "pleroma",
                    "key" => "key3",
                    "value" => [
-                     [%{"nested_3" => "nested_3"}, %{"nested_33" => "nested_33"}],
+                     %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
                      %{"nested_4" => true}
                    ]
                  },
                  %{
                    "group" => "pleroma",
                    "key" => "key4",
-                   "value" => [%{"endpoint" => "https://example.com"}, %{"nested_5" => "upload"}]
+                   "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"}
                  },
                  %{
                    "group" => "idna",
@@ -1483,23 +1490,23 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
       assert Application.get_env(:pleroma, :key1) == "value1"
 
-      assert Application.get_env(:pleroma, :key2) == [
+      assert Application.get_env(:pleroma, :key2) == %{
                nested_1: "nested_value1",
                nested_2: [
-                 [nested_22: "nested_value222"],
-                 [nested_33: [nested_44: "nested_444"]]
+                 %{nested_22: "nested_value222"},
+                 %{nested_33: %{nested_44: "nested_444"}}
                ]
-             ]
+             }
 
       assert Application.get_env(:pleroma, :key3) == [
-               [nested_3: :nested_3, nested_33: "nested_33"],
-               [nested_4: true]
+               %{"nested_3" => :nested_3, "nested_33" => "nested_33"},
+               %{"nested_4" => true}
              ]
 
-      assert Application.get_env(:pleroma, :key4) == [
-               endpoint: "https://example.com",
+      assert Application.get_env(:pleroma, :key4) == %{
+               "endpoint" => "https://example.com",
                nested_5: :upload
-             ]
+             }
 
       assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
     end
@@ -1508,11 +1515,22 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       config1 = insert(:config, key: "keyaa1")
       config2 = insert(:config, key: "keyaa2")
 
+      insert(:config,
+        group: "ueberauth",
+        key: "Ueberauth.Strategy.Microsoft.OAuth",
+        value: :erlang.term_to_binary([])
+      )
+
       conn =
         post(conn, "/api/pleroma/admin/config", %{
           configs: [
             %{group: config1.group, key: config1.key, value: "another_value"},
-            %{group: config2.group, key: config2.key, delete: "true"}
+            %{group: config2.group, key: config2.key, delete: "true"},
+            %{
+              group: "ueberauth",
+              key: "Ueberauth.Strategy.Microsoft.OAuth",
+              delete: "true"
+            }
           ]
         })
 
@@ -1537,11 +1555,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
             %{
               "group" => "pleroma",
               "key" => "Pleroma.Captcha.NotReal",
-              "value" => %{
-                "enabled" => ":false",
-                "method" => "Pleroma.Captcha.Kocaptcha",
-                "seconds_valid" => "i:60"
-              }
+              "value" => [
+                %{"tuple" => [":enabled", false]},
+                %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
+                %{"tuple" => [":seconds_valid", 60]},
+                %{"tuple" => [":path", ""]},
+                %{"tuple" => [":key1", nil]}
+              ]
             }
           ]
         })
@@ -1552,9 +1572,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    "group" => "pleroma",
                    "key" => "Pleroma.Captcha.NotReal",
                    "value" => [
-                     %{"enabled" => false},
-                     %{"method" => "Pleroma.Captcha.Kocaptcha"},
-                     %{"seconds_valid" => 60}
+                     %{"tuple" => [":enabled", false]},
+                     %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
+                     %{"tuple" => [":seconds_valid", 60]},
+                     %{"tuple" => [":path", ""]},
+                     %{"tuple" => [":key1", nil]}
                    ]
                  }
                ]
@@ -1570,51 +1592,57 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
               "key" => "Pleroma.Web.Endpoint.NotReal",
               "value" => [
                 %{
-                  "http" => %{
-                    "dispatch" => [
+                  "tuple" => [
+                    ":http",
+                    [
                       %{
                         "tuple" => [
-                          ":_",
+                          ":key2",
                           [
-                            %{
-                              "tuple" => [
-                                "/api/v1/streaming",
-                                "Pleroma.Web.MastodonAPI.WebsocketHandler",
-                                []
-                              ]
-                            },
-                            %{
-                              "tuple" => [
-                                "/websocket",
-                                "Phoenix.Endpoint.CowboyWebSocket",
-                                %{
-                                  "tuple" => [
-                                    "Phoenix.Transports.WebSocket",
-                                    %{
-                                      "tuple" => [
-                                        "Pleroma.Web.Endpoint",
-                                        "Pleroma.Web.UserSocket",
-                                        []
-                                      ]
-                                    }
-                                  ]
-                                }
-                              ]
-                            },
                             %{
                               "tuple" => [
                                 ":_",
-                                "Phoenix.Endpoint.Cowboy2Handler",
-                                %{
-                                  "tuple" => ["Pleroma.Web.Endpoint", []]
-                                }
+                                [
+                                  %{
+                                    "tuple" => [
+                                      "/api/v1/streaming",
+                                      "Pleroma.Web.MastodonAPI.WebsocketHandler",
+                                      []
+                                    ]
+                                  },
+                                  %{
+                                    "tuple" => [
+                                      "/websocket",
+                                      "Phoenix.Endpoint.CowboyWebSocket",
+                                      %{
+                                        "tuple" => [
+                                          "Phoenix.Transports.WebSocket",
+                                          %{
+                                            "tuple" => [
+                                              "Pleroma.Web.Endpoint",
+                                              "Pleroma.Web.UserSocket",
+                                              []
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  },
+                                  %{
+                                    "tuple" => [
+                                      ":_",
+                                      "Phoenix.Endpoint.Cowboy2Handler",
+                                      %{"tuple" => ["Pleroma.Web.Endpoint", []]}
+                                    ]
+                                  }
+                                ]
                               ]
                             }
                           ]
                         ]
                       }
                     ]
-                  }
+                  ]
                 }
               ]
             }
@@ -1628,41 +1656,206 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    "key" => "Pleroma.Web.Endpoint.NotReal",
                    "value" => [
                      %{
-                       "http" => %{
-                         "dispatch" => %{
-                           "_" => [
-                             %{
-                               "tuple" => [
-                                 "/api/v1/streaming",
-                                 "Pleroma.Web.MastodonAPI.WebsocketHandler",
-                                 []
-                               ]
-                             },
-                             %{
-                               "tuple" => [
-                                 "/websocket",
-                                 "Phoenix.Endpoint.CowboyWebSocket",
+                       "tuple" => [
+                         ":http",
+                         [
+                           %{
+                             "tuple" => [
+                               ":key2",
+                               [
                                  %{
-                                   "Elixir.Phoenix.Transports.WebSocket" => %{
-                                     "tuple" => [
-                                       "Pleroma.Web.Endpoint",
-                                       "Pleroma.Web.UserSocket",
-                                       []
+                                   "tuple" => [
+                                     ":_",
+                                     [
+                                       %{
+                                         "tuple" => [
+                                           "/api/v1/streaming",
+                                           "Pleroma.Web.MastodonAPI.WebsocketHandler",
+                                           []
+                                         ]
+                                       },
+                                       %{
+                                         "tuple" => [
+                                           "/websocket",
+                                           "Phoenix.Endpoint.CowboyWebSocket",
+                                           %{
+                                             "tuple" => [
+                                               "Phoenix.Transports.WebSocket",
+                                               %{
+                                                 "tuple" => [
+                                                   "Pleroma.Web.Endpoint",
+                                                   "Pleroma.Web.UserSocket",
+                                                   []
+                                                 ]
+                                               }
+                                             ]
+                                           }
+                                         ]
+                                       },
+                                       %{
+                                         "tuple" => [
+                                           ":_",
+                                           "Phoenix.Endpoint.Cowboy2Handler",
+                                           %{"tuple" => ["Pleroma.Web.Endpoint", []]}
+                                         ]
+                                       }
                                      ]
-                                   }
+                                   ]
                                  }
                                ]
-                             },
-                             %{
-                               "tuple" => [
-                                 "_",
-                                 "Phoenix.Endpoint.Cowboy2Handler",
-                                 %{"Elixir.Pleroma.Web.Endpoint" => []}
-                               ]
+                             ]
+                           }
+                         ]
+                       ]
+                     }
+                   ]
+                 }
+               ]
+             }
+    end
+
+    test "settings with nesting map", %{conn: conn} do
+      conn =
+        post(conn, "/api/pleroma/admin/config", %{
+          configs: [
+            %{
+              "group" => "pleroma",
+              "key" => "key1",
+              "value" => [
+                %{"tuple" => [":key2", "some_val"]},
+                %{
+                  "tuple" => [
+                    ":key3",
+                    %{
+                      ":max_options" => 20,
+                      ":max_option_chars" => 200,
+                      ":min_expiration" => 0,
+                      ":max_expiration" => 31_536_000,
+                      "nested" => %{
+                        ":max_options" => 20,
+                        ":max_option_chars" => 200,
+                        ":min_expiration" => 0,
+                        ":max_expiration" => 31_536_000
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        })
+
+      assert json_response(conn, 200) ==
+               %{
+                 "configs" => [
+                   %{
+                     "group" => "pleroma",
+                     "key" => "key1",
+                     "value" => [
+                       %{"tuple" => [":key2", "some_val"]},
+                       %{
+                         "tuple" => [
+                           ":key3",
+                           %{
+                             ":max_expiration" => 31_536_000,
+                             ":max_option_chars" => 200,
+                             ":max_options" => 20,
+                             ":min_expiration" => 0,
+                             "nested" => %{
+                               ":max_expiration" => 31_536_000,
+                               ":max_option_chars" => 200,
+                               ":max_options" => 20,
+                               ":min_expiration" => 0
                              }
-                           ]
-                         }
+                           }
+                         ]
                        }
+                     ]
+                   }
+                 ]
+               }
+    end
+
+    test "value as map", %{conn: conn} do
+      conn =
+        post(conn, "/api/pleroma/admin/config", %{
+          configs: [
+            %{
+              "group" => "pleroma",
+              "key" => "key1",
+              "value" => %{"key" => "some_val"}
+            }
+          ]
+        })
+
+      assert json_response(conn, 200) ==
+               %{
+                 "configs" => [
+                   %{
+                     "group" => "pleroma",
+                     "key" => "key1",
+                     "value" => %{"key" => "some_val"}
+                   }
+                 ]
+               }
+    end
+
+    test "dispatch setting", %{conn: conn} do
+      conn =
+        post(conn, "/api/pleroma/admin/config", %{
+          configs: [
+            %{
+              "group" => "pleroma",
+              "key" => "Pleroma.Web.Endpoint.NotReal",
+              "value" => [
+                %{
+                  "tuple" => [
+                    ":http",
+                    [
+                      %{"tuple" => [":ip", %{"tuple" => [127, 0, 0, 1]}]},
+                      %{"tuple" => [":dispatch", ["{:_,
+       [
+         {\"/api/v1/streaming\", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
+         {\"/websocket\", Phoenix.Endpoint.CowboyWebSocket,
+          {Phoenix.Transports.WebSocket,
+           {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: \"/websocket\"]}}},
+         {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
+       ]}"]]}
+                    ]
+                  ]
+                }
+              ]
+            }
+          ]
+        })
+
+      dispatch_string =
+        "{:_, [{\"/api/v1/streaming\", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, " <>
+          "{\"/websocket\", Phoenix.Endpoint.CowboyWebSocket, {Phoenix.Transports.WebSocket, " <>
+          "{Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: \"/websocket\"]}}}, " <>
+          "{:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}]}"
+
+      assert json_response(conn, 200) == %{
+               "configs" => [
+                 %{
+                   "group" => "pleroma",
+                   "key" => "Pleroma.Web.Endpoint.NotReal",
+                   "value" => [
+                     %{
+                       "tuple" => [
+                         ":http",
+                         [
+                           %{"tuple" => [":ip", %{"tuple" => [127, 0, 0, 1]}]},
+                           %{
+                             "tuple" => [
+                               ":dispatch",
+                               [
+                                 dispatch_string
+                               ]
+                             ]
+                           }
+                         ]
+                       ]
                      }
                    ]
                  }
index 10cb3b68a859effe119877aeabc60926899a0836..d41666ef3e0fbb425d3c74269c8b6ee0f5062284 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.AdminAPI.ConfigTest do
   use Pleroma.DataCase, async: true
   import Pleroma.Factory
@@ -57,117 +61,306 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do
       assert Config.from_binary(binary) == "value as string"
     end
 
+    test "boolean" do
+      binary = Config.transform(false)
+      assert binary == :erlang.term_to_binary(false)
+      assert Config.from_binary(binary) == false
+    end
+
+    test "nil" do
+      binary = Config.transform(nil)
+      assert binary == :erlang.term_to_binary(nil)
+      assert Config.from_binary(binary) == nil
+    end
+
+    test "integer" do
+      binary = Config.transform(150)
+      assert binary == :erlang.term_to_binary(150)
+      assert Config.from_binary(binary) == 150
+    end
+
+    test "atom" do
+      binary = Config.transform(":atom")
+      assert binary == :erlang.term_to_binary(:atom)
+      assert Config.from_binary(binary) == :atom
+    end
+
+    test "pleroma module" do
+      binary = Config.transform("Pleroma.Bookmark")
+      assert binary == :erlang.term_to_binary(Pleroma.Bookmark)
+      assert Config.from_binary(binary) == Pleroma.Bookmark
+    end
+
+    test "phoenix module" do
+      binary = Config.transform("Phoenix.Socket.V1.JSONSerializer")
+      assert binary == :erlang.term_to_binary(Phoenix.Socket.V1.JSONSerializer)
+      assert Config.from_binary(binary) == Phoenix.Socket.V1.JSONSerializer
+    end
+
+    test "sigil" do
+      binary = Config.transform("~r/comp[lL][aA][iI][nN]er/")
+      assert binary == :erlang.term_to_binary(~r/comp[lL][aA][iI][nN]er/)
+      assert Config.from_binary(binary) == ~r/comp[lL][aA][iI][nN]er/
+    end
+
+    test "2 child tuple" do
+      binary = Config.transform(%{"tuple" => ["v1", ":v2"]})
+      assert binary == :erlang.term_to_binary({"v1", :v2})
+      assert Config.from_binary(binary) == {"v1", :v2}
+    end
+
+    test "tuple with n childs" do
+      binary =
+        Config.transform(%{
+          "tuple" => [
+            "v1",
+            ":v2",
+            "Pleroma.Bookmark",
+            150,
+            false,
+            "Phoenix.Socket.V1.JSONSerializer"
+          ]
+        })
+
+      assert binary ==
+               :erlang.term_to_binary(
+                 {"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer}
+               )
+
+      assert Config.from_binary(binary) ==
+               {"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer}
+    end
+
+    test "tuple with dispatch key" do
+      binary = Config.transform(%{"tuple" => [":dispatch", ["{:_,
+       [
+         {\"/api/v1/streaming\", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
+         {\"/websocket\", Phoenix.Endpoint.CowboyWebSocket,
+          {Phoenix.Transports.WebSocket,
+           {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: \"/websocket\"]}}},
+         {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
+       ]}"]]})
+
+      assert binary ==
+               :erlang.term_to_binary(
+                 {:dispatch,
+                  [
+                    {:_,
+                     [
+                       {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
+                       {"/websocket", Phoenix.Endpoint.CowboyWebSocket,
+                        {Phoenix.Transports.WebSocket,
+                         {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: "/websocket"]}}},
+                       {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
+                     ]}
+                  ]}
+               )
+
+      assert Config.from_binary(binary) ==
+               {:dispatch,
+                [
+                  {:_,
+                   [
+                     {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
+                     {"/websocket", Phoenix.Endpoint.CowboyWebSocket,
+                      {Phoenix.Transports.WebSocket,
+                       {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: "/websocket"]}}},
+                     {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
+                   ]}
+                ]}
+    end
+
+    test "map with string key" do
+      binary = Config.transform(%{"key" => "value"})
+      assert binary == :erlang.term_to_binary(%{"key" => "value"})
+      assert Config.from_binary(binary) == %{"key" => "value"}
+    end
+
+    test "map with atom key" do
+      binary = Config.transform(%{":key" => "value"})
+      assert binary == :erlang.term_to_binary(%{key: "value"})
+      assert Config.from_binary(binary) == %{key: "value"}
+    end
+
+    test "list of strings" do
+      binary = Config.transform(["v1", "v2", "v3"])
+      assert binary == :erlang.term_to_binary(["v1", "v2", "v3"])
+      assert Config.from_binary(binary) == ["v1", "v2", "v3"]
+    end
+
     test "list of modules" do
       binary = Config.transform(["Pleroma.Repo", "Pleroma.Activity"])
       assert binary == :erlang.term_to_binary([Pleroma.Repo, Pleroma.Activity])
       assert Config.from_binary(binary) == [Pleroma.Repo, Pleroma.Activity]
     end
 
-    test "list of strings" do
-      binary = Config.transform(["string1", "string2"])
-      assert binary == :erlang.term_to_binary(["string1", "string2"])
-      assert Config.from_binary(binary) == ["string1", "string2"]
+    test "list of atoms" do
+      binary = Config.transform([":v1", ":v2", ":v3"])
+      assert binary == :erlang.term_to_binary([:v1, :v2, :v3])
+      assert Config.from_binary(binary) == [:v1, :v2, :v3]
     end
 
-    test "map" do
+    test "list of mixed values" do
       binary =
-        Config.transform(%{
-          "types" => "Pleroma.PostgresTypes",
-          "telemetry_event" => ["Pleroma.Repo.Instrumenter"],
-          "migration_lock" => ""
-        })
+        Config.transform([
+          "v1",
+          ":v2",
+          "Pleroma.Repo",
+          "Phoenix.Socket.V1.JSONSerializer",
+          15,
+          false
+        ])
+
+      assert binary ==
+               :erlang.term_to_binary([
+                 "v1",
+                 :v2,
+                 Pleroma.Repo,
+                 Phoenix.Socket.V1.JSONSerializer,
+                 15,
+                 false
+               ])
+
+      assert Config.from_binary(binary) == [
+               "v1",
+               :v2,
+               Pleroma.Repo,
+               Phoenix.Socket.V1.JSONSerializer,
+               15,
+               false
+             ]
+    end
+
+    test "simple keyword" do
+      binary = Config.transform([%{"tuple" => [":key", "value"]}])
+      assert binary == :erlang.term_to_binary([{:key, "value"}])
+      assert Config.from_binary(binary) == [{:key, "value"}]
+      assert Config.from_binary(binary) == [key: "value"]
+    end
+
+    test "keyword" do
+      binary =
+        Config.transform([
+          %{"tuple" => [":types", "Pleroma.PostgresTypes"]},
+          %{"tuple" => [":telemetry_event", ["Pleroma.Repo.Instrumenter"]]},
+          %{"tuple" => [":migration_lock", nil]},
+          %{"tuple" => [":key1", 150]},
+          %{"tuple" => [":key2", "string"]}
+        ])
 
       assert binary ==
                :erlang.term_to_binary(
+                 types: Pleroma.PostgresTypes,
                  telemetry_event: [Pleroma.Repo.Instrumenter],
-                 types: Pleroma.PostgresTypes
+                 migration_lock: nil,
+                 key1: 150,
+                 key2: "string"
                )
 
       assert Config.from_binary(binary) == [
+               types: Pleroma.PostgresTypes,
                telemetry_event: [Pleroma.Repo.Instrumenter],
-               types: Pleroma.PostgresTypes
+               migration_lock: nil,
+               key1: 150,
+               key2: "string"
              ]
     end
 
-    test "complex map with nested integers, lists and atoms" do
+    test "complex keyword with nested mixed childs" do
       binary =
-        Config.transform(%{
-          "uploader" => "Pleroma.Uploaders.Local",
-          "filters" => ["Pleroma.Upload.Filter.Dedupe"],
-          "link_name" => ":true",
-          "proxy_remote" => ":false",
-          "proxy_opts" => %{
-            "redirect_on_failure" => ":false",
-            "max_body_length" => "i:1048576",
-            "http" => %{
-              "follow_redirect" => ":true",
-              "pool" => ":upload"
-            }
+        Config.transform([
+          %{"tuple" => [":uploader", "Pleroma.Uploaders.Local"]},
+          %{"tuple" => [":filters", ["Pleroma.Upload.Filter.Dedupe"]]},
+          %{"tuple" => [":link_name", true]},
+          %{"tuple" => [":proxy_remote", false]},
+          %{"tuple" => [":common_map", %{":key" => "value"}]},
+          %{
+            "tuple" => [
+              ":proxy_opts",
+              [
+                %{"tuple" => [":redirect_on_failure", false]},
+                %{"tuple" => [":max_body_length", 1_048_576]},
+                %{
+                  "tuple" => [
+                    ":http",
+                    [%{"tuple" => [":follow_redirect", true]}, %{"tuple" => [":pool", ":upload"]}]
+                  ]
+                }
+              ]
+            ]
           }
-        })
+        ])
 
       assert binary ==
                :erlang.term_to_binary(
+                 uploader: Pleroma.Uploaders.Local,
                  filters: [Pleroma.Upload.Filter.Dedupe],
                  link_name: true,
+                 proxy_remote: false,
+                 common_map: %{key: "value"},
                  proxy_opts: [
+                   redirect_on_failure: false,
+                   max_body_length: 1_048_576,
                    http: [
                      follow_redirect: true,
                      pool: :upload
-                   ],
-                   max_body_length: 1_048_576,
-                   redirect_on_failure: false
-                 ],
-                 proxy_remote: false,
-                 uploader: Pleroma.Uploaders.Local
+                   ]
+                 ]
                )
 
       assert Config.from_binary(binary) ==
                [
+                 uploader: Pleroma.Uploaders.Local,
                  filters: [Pleroma.Upload.Filter.Dedupe],
                  link_name: true,
+                 proxy_remote: false,
+                 common_map: %{key: "value"},
                  proxy_opts: [
+                   redirect_on_failure: false,
+                   max_body_length: 1_048_576,
                    http: [
                      follow_redirect: true,
                      pool: :upload
-                   ],
-                   max_body_length: 1_048_576,
-                   redirect_on_failure: false
-                 ],
-                 proxy_remote: false,
-                 uploader: Pleroma.Uploaders.Local
+                   ]
+                 ]
                ]
     end
 
-    test "keyword" do
+    test "common keyword" do
       binary =
-        Config.transform(%{
-          "level" => ":warn",
-          "meta" => [":all"],
-          "webhook_url" => "https://hooks.slack.com/services/YOUR-KEY-HERE"
-        })
+        Config.transform([
+          %{"tuple" => [":level", ":warn"]},
+          %{"tuple" => [":meta", [":all"]]},
+          %{"tuple" => [":path", ""]},
+          %{"tuple" => [":val", nil]},
+          %{"tuple" => [":webhook_url", "https://hooks.slack.com/services/YOUR-KEY-HERE"]}
+        ])
 
       assert binary ==
                :erlang.term_to_binary(
                  level: :warn,
                  meta: [:all],
+                 path: "",
+                 val: nil,
                  webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
                )
 
       assert Config.from_binary(binary) == [
                level: :warn,
                meta: [:all],
+               path: "",
+               val: nil,
                webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
              ]
     end
 
-    test "complex map with sigil" do
+    test "complex keyword with sigil" do
       binary =
-        Config.transform(%{
-          federated_timeline_removal: [],
-          reject: [~r/comp[lL][aA][iI][nN]er/],
-          replace: []
-        })
+        Config.transform([
+          %{"tuple" => [":federated_timeline_removal", []]},
+          %{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]},
+          %{"tuple" => [":replace", []]}
+        ])
 
       assert binary ==
                :erlang.term_to_binary(
@@ -180,54 +373,68 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do
                [federated_timeline_removal: [], reject: [~r/comp[lL][aA][iI][nN]er/], replace: []]
     end
 
-    test "complex map with tuples with more than 2 values" do
+    test "complex keyword with tuples with more than 2 values" do
       binary =
-        Config.transform(%{
-          "http" => %{
-            "dispatch" => [
-              %{
-                "tuple" => [
-                  ":_",
-                  [
-                    %{
-                      "tuple" => [
-                        "/api/v1/streaming",
-                        "Pleroma.Web.MastodonAPI.WebsocketHandler",
-                        []
-                      ]
-                    },
-                    %{
-                      "tuple" => [
-                        "/websocket",
-                        "Phoenix.Endpoint.CowboyWebSocket",
-                        %{
-                          "tuple" => [
-                            "Phoenix.Transports.WebSocket",
-                            %{"tuple" => ["Pleroma.Web.Endpoint", "Pleroma.Web.UserSocket", []]}
+        Config.transform([
+          %{
+            "tuple" => [
+              ":http",
+              [
+                %{
+                  "tuple" => [
+                    ":key1",
+                    [
+                      %{
+                        "tuple" => [
+                          ":_",
+                          [
+                            %{
+                              "tuple" => [
+                                "/api/v1/streaming",
+                                "Pleroma.Web.MastodonAPI.WebsocketHandler",
+                                []
+                              ]
+                            },
+                            %{
+                              "tuple" => [
+                                "/websocket",
+                                "Phoenix.Endpoint.CowboyWebSocket",
+                                %{
+                                  "tuple" => [
+                                    "Phoenix.Transports.WebSocket",
+                                    %{
+                                      "tuple" => [
+                                        "Pleroma.Web.Endpoint",
+                                        "Pleroma.Web.UserSocket",
+                                        []
+                                      ]
+                                    }
+                                  ]
+                                }
+                              ]
+                            },
+                            %{
+                              "tuple" => [
+                                ":_",
+                                "Phoenix.Endpoint.Cowboy2Handler",
+                                %{"tuple" => ["Pleroma.Web.Endpoint", []]}
+                              ]
+                            }
                           ]
-                        }
-                      ]
-                    },
-                    %{
-                      "tuple" => [
-                        ":_",
-                        "Phoenix.Endpoint.Cowboy2Handler",
-                        %{
-                          "tuple" => ["Pleroma.Web.Endpoint", []]
-                        }
-                      ]
-                    }
+                        ]
+                      }
+                    ]
                   ]
-                ]
-              }
+                }
+              ]
             ]
           }
-        })
+        ])
 
       assert binary ==
                :erlang.term_to_binary(
                  http: [
-                   dispatch: [
+                   key1: [
                      _: [
                        {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
                        {"/websocket", Phoenix.Endpoint.CowboyWebSocket,
@@ -241,7 +448,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do
 
       assert Config.from_binary(binary) == [
                http: [
-                 dispatch: [
+                 key1: [
                    {:_,
                     [
                       {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
index 6f57bbe1f7b0d568e0852007de286dacad50391b..958c931c49a21353d6cdf5f9ac8a286ec1f3c1e0 100644 (file)
@@ -34,7 +34,7 @@ defmodule Pleroma.Web.CommonAPITest do
     user = insert(:user)
     {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu #2HU"})
 
-    object = Object.normalize(activity.data["object"])
+    object = Object.normalize(activity)
 
     assert object.data["tag"] == ["2hu"]
   end
@@ -87,7 +87,7 @@ defmodule Pleroma.Web.CommonAPITest do
           "content_type" => "text/html"
         })
 
-      object = Object.normalize(activity.data["object"])
+      object = Object.normalize(activity)
 
       assert object.data["content"] == "<p><b>2hu</b></p>alert('xss')"
     end
@@ -103,7 +103,7 @@ defmodule Pleroma.Web.CommonAPITest do
           "content_type" => "text/markdown"
         })
 
-      object = Object.normalize(activity.data["object"])
+      object = Object.normalize(activity)
 
       assert object.data["content"] == "<p><b>2hu</b></p>alert('xss')"
     end
index 64f14f794c275cbdbc2a31794f449086bcbdd2cf..8afb1497be4450b85763c19797ae5380824cbb28 100644 (file)
@@ -2958,6 +2958,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
       assert Map.has_key?(emoji, "static_url")
       assert Map.has_key?(emoji, "tags")
       assert is_list(emoji["tags"])
+      assert Map.has_key?(emoji, "category")
       assert Map.has_key?(emoji, "url")
       assert Map.has_key?(emoji, "visible_in_picker")
     end
index c3f53159049dceb94ba7ef57fe55350131218488..9f50c09f49fbb1d03898fac33fbbd614ef9a3dad 100644 (file)
@@ -6,123 +6,262 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
   use Pleroma.Web.ConnCase
 
   alias Pleroma.Object
+  alias Pleroma.Web
   alias Pleroma.Web.CommonAPI
   import Pleroma.Factory
   import ExUnit.CaptureLog
   import Tesla.Mock
+  import Mock
 
   setup do
-    mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
+    mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
     :ok
   end
 
-  test "account search", %{conn: conn} do
-    user = insert(:user)
-    user_two = insert(:user, %{nickname: "shp@shitposter.club"})
-    user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
+  describe ".search2" do
+    test "it returns empty result if user or status search return undefined error", %{conn: conn} do
+      with_mocks [
+        {Pleroma.User, [], [search: fn _q, _o -> raise "Oops" end]},
+        {Pleroma.Activity, [], [search: fn _u, _q, _o -> raise "Oops" end]}
+      ] do
+        conn = get(conn, "/api/v2/search", %{"q" => "2hu"})
 
-    results =
-      conn
-      |> assign(:user, user)
-      |> get("/api/v1/accounts/search", %{"q" => "shp"})
-      |> json_response(200)
+        assert results = json_response(conn, 200)
 
-    result_ids = for result <- results, do: result["acct"]
+        assert results["accounts"] == []
+        assert results["statuses"] == []
+      end
+    end
 
-    assert user_two.nickname in result_ids
-    assert user_three.nickname in result_ids
+    test "search", %{conn: conn} do
+      user = insert(:user)
+      user_two = insert(:user, %{nickname: "shp@shitposter.club"})
+      user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
 
-    results =
-      conn
-      |> assign(:user, user)
-      |> get("/api/v1/accounts/search", %{"q" => "2hu"})
-      |> json_response(200)
+      {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu private"})
 
-    result_ids = for result <- results, do: result["acct"]
+      {:ok, _activity} =
+        CommonAPI.post(user, %{
+          "status" => "This is about 2hu, but private",
+          "visibility" => "private"
+        })
 
-    assert user_three.nickname in result_ids
-  end
+      {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
 
-  test "search", %{conn: conn} do
-    user = insert(:user)
-    user_two = insert(:user, %{nickname: "shp@shitposter.club"})
-    user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
+      conn = get(conn, "/api/v2/search", %{"q" => "2hu #private"})
 
-    {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
+      assert results = json_response(conn, 200)
 
-    {:ok, _activity} =
-      CommonAPI.post(user, %{
-        "status" => "This is about 2hu, but private",
-        "visibility" => "private"
-      })
+      [account | _] = results["accounts"]
+      assert account["id"] == to_string(user_three.id)
 
-    {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
+      assert results["hashtags"] == [
+               %{"name" => "private", "url" => "#{Web.base_url()}/tag/private"}
+             ]
 
-    conn =
-      conn
-      |> get("/api/v1/search", %{"q" => "2hu"})
+      [status] = results["statuses"]
+      assert status["id"] == to_string(activity.id)
+    end
+  end
 
-    assert results = json_response(conn, 200)
+  describe ".account_search" do
+    test "account search", %{conn: conn} do
+      user = insert(:user)
+      user_two = insert(:user, %{nickname: "shp@shitposter.club"})
+      user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
 
-    [account | _] = results["accounts"]
-    assert account["id"] == to_string(user_three.id)
+      results =
+        conn
+        |> assign(:user, user)
+        |> get("/api/v1/accounts/search", %{"q" => "shp"})
+        |> json_response(200)
+
+      result_ids = for result <- results, do: result["acct"]
 
-    assert results["hashtags"] == []
+      assert user_two.nickname in result_ids
+      assert user_three.nickname in result_ids
 
-    [status] = results["statuses"]
-    assert status["id"] == to_string(activity.id)
+      results =
+        conn
+        |> assign(:user, user)
+        |> get("/api/v1/accounts/search", %{"q" => "2hu"})
+        |> json_response(200)
+
+      result_ids = for result <- results, do: result["acct"]
+
+      assert user_three.nickname in result_ids
+    end
   end
 
-  test "search fetches remote statuses", %{conn: conn} do
-    capture_log(fn ->
+  describe ".search" do
+    test "it returns empty result if user or status search return undefined error", %{conn: conn} do
+      with_mocks [
+        {Pleroma.User, [], [search: fn _q, _o -> raise "Oops" end]},
+        {Pleroma.Activity, [], [search: fn _u, _q, _o -> raise "Oops" end]}
+      ] do
+        conn =
+          conn
+          |> get("/api/v1/search", %{"q" => "2hu"})
+
+        assert results = json_response(conn, 200)
+
+        assert results["accounts"] == []
+        assert results["statuses"] == []
+      end
+    end
+
+    test "search", %{conn: conn} do
+      user = insert(:user)
+      user_two = insert(:user, %{nickname: "shp@shitposter.club"})
+      user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
+
+      {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
+
+      {:ok, _activity} =
+        CommonAPI.post(user, %{
+          "status" => "This is about 2hu, but private",
+          "visibility" => "private"
+        })
+
+      {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
+
       conn =
         conn
-        |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
+        |> get("/api/v1/search", %{"q" => "2hu"})
 
       assert results = json_response(conn, 200)
 
+      [account | _] = results["accounts"]
+      assert account["id"] == to_string(user_three.id)
+
+      assert results["hashtags"] == []
+
       [status] = results["statuses"]
-      assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
-    end)
-  end
+      assert status["id"] == to_string(activity.id)
+    end
+
+    test "search fetches remote statuses", %{conn: conn} do
+      capture_log(fn ->
+        conn =
+          conn
+          |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
+
+        assert results = json_response(conn, 200)
+
+        [status] = results["statuses"]
+
+        assert status["uri"] ==
+                 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
+      end)
+    end
+
+    test "search doesn't show statuses that it shouldn't", %{conn: conn} do
+      {:ok, activity} =
+        CommonAPI.post(insert(:user), %{
+          "status" => "This is about 2hu, but private",
+          "visibility" => "private"
+        })
+
+      capture_log(fn ->
+        conn =
+          conn
+          |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
 
-  test "search doesn't show statuses that it shouldn't", %{conn: conn} do
-    {:ok, activity} =
-      CommonAPI.post(insert(:user), %{
-        "status" => "This is about 2hu, but private",
-        "visibility" => "private"
-      })
+        assert results = json_response(conn, 200)
+
+        [] = results["statuses"]
+      end)
+    end
+
+    test "search fetches remote accounts", %{conn: conn} do
+      user = insert(:user)
 
-    capture_log(fn ->
       conn =
         conn
-        |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
+        |> assign(:user, user)
+        |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
 
       assert results = json_response(conn, 200)
+      [account] = results["accounts"]
+      assert account["acct"] == "shp@social.heldscal.la"
+    end
 
-      [] = results["statuses"]
-    end)
-  end
+    test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do
+      conn =
+        conn
+        |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "false"})
+
+      assert results = json_response(conn, 200)
+      assert [] == results["accounts"]
+    end
 
-  test "search fetches remote accounts", %{conn: conn} do
-    user = insert(:user)
+    test "search with limit and offset", %{conn: conn} do
+      user = insert(:user)
+      _user_two = insert(:user, %{nickname: "shp@shitposter.club"})
+      _user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
 
-    conn =
-      conn
-      |> assign(:user, user)
-      |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
+      {:ok, _activity1} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
+      {:ok, _activity2} = CommonAPI.post(user, %{"status" => "This is also about 2hu"})
 
-    assert results = json_response(conn, 200)
-    [account] = results["accounts"]
-    assert account["acct"] == "shp@social.heldscal.la"
-  end
+      result =
+        conn
+        |> get("/api/v1/search", %{"q" => "2hu", "limit" => 1})
+
+      assert results = json_response(result, 200)
+      assert [%{"id" => activity_id1}] = results["statuses"]
+      assert [_] = results["accounts"]
+
+      results =
+        conn
+        |> get("/api/v1/search", %{"q" => "2hu", "limit" => 1, "offset" => 1})
+        |> json_response(200)
 
-  test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do
-    conn =
-      conn
-      |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "false"})
+      assert [%{"id" => activity_id2}] = results["statuses"]
+      assert [] = results["accounts"]
+
+      assert activity_id1 != activity_id2
+    end
+
+    test "search returns results only for the given type", %{conn: conn} do
+      user = insert(:user)
+      _user_two = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
+
+      {:ok, _activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
+
+      assert %{"statuses" => [_activity], "accounts" => [], "hashtags" => []} =
+               conn
+               |> get("/api/v1/search", %{"q" => "2hu", "type" => "statuses"})
+               |> json_response(200)
+
+      assert %{"statuses" => [], "accounts" => [_user_two], "hashtags" => []} =
+               conn
+               |> get("/api/v1/search", %{"q" => "2hu", "type" => "accounts"})
+               |> json_response(200)
+    end
+
+    test "search uses account_id to filter statuses by the author", %{conn: conn} do
+      user = insert(:user, %{nickname: "shp@shitposter.club"})
+      user_two = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
+
+      {:ok, activity1} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
+      {:ok, activity2} = CommonAPI.post(user_two, %{"status" => "This is also about 2hu"})
+
+      results =
+        conn
+        |> get("/api/v1/search", %{"q" => "2hu", "account_id" => user.id})
+        |> json_response(200)
+
+      assert [%{"id" => activity_id1}] = results["statuses"]
+      assert activity_id1 == activity1.id
+      assert [_] = results["accounts"]
+
+      results =
+        conn
+        |> get("/api/v1/search", %{"q" => "2hu", "account_id" => user_two.id})
+        |> json_response(200)
 
-    assert results = json_response(conn, 200)
-    assert [] == results["accounts"]
+      assert [%{"id" => activity_id2}] = results["statuses"]
+      assert activity_id2 == activity2.id
+    end
   end
 end
index 73791a95b5f4fcfede475d03a13089ebd2647cc6..ac42819d8e4a20ac278f8aff10d88b1f1b554885 100644 (file)
@@ -55,7 +55,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
 
   test "a note with null content" do
     note = insert(:note_activity)
-    note_object = Object.normalize(note.data["object"])
+    note_object = Object.normalize(note)
 
     data =
       note_object.data
@@ -73,26 +73,27 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
 
   test "a note activity" do
     note = insert(:note_activity)
+    object_data = Object.normalize(note).data
     user = User.get_cached_by_ap_id(note.data["actor"])
 
-    convo_id = Utils.context_to_conversation_id(note.data["object"]["context"])
+    convo_id = Utils.context_to_conversation_id(object_data["context"])
 
     status = StatusView.render("status.json", %{activity: note})
 
     created_at =
-      (note.data["object"]["published"] || "")
+      (object_data["published"] || "")
       |> String.replace(~r/\.\d+Z/, ".000Z")
 
     expected = %{
       id: to_string(note.id),
-      uri: note.data["object"]["id"],
+      uri: object_data["id"],
       url: Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, note),
       account: AccountView.render("account.json", %{user: user}),
       in_reply_to_id: nil,
       in_reply_to_account_id: nil,
       card: nil,
       reblog: nil,
-      content: HtmlSanitizeEx.basic_html(note.data["object"]["content"]),
+      content: HtmlSanitizeEx.basic_html(object_data["content"]),
       created_at: created_at,
       reblogs_count: 0,
       replies_count: 0,
@@ -104,14 +105,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
       pinned: false,
       sensitive: false,
       poll: nil,
-      spoiler_text: HtmlSanitizeEx.basic_html(note.data["object"]["summary"]),
+      spoiler_text: HtmlSanitizeEx.basic_html(object_data["summary"]),
       visibility: "public",
       media_attachments: [],
       mentions: [],
       tags: [
         %{
-          name: "#{note.data["object"]["tag"]}",
-          url: "/tag/#{note.data["object"]["tag"]}"
+          name: "#{object_data["tag"]}",
+          url: "/tag/#{object_data["tag"]}"
         }
       ],
       application: %{
@@ -131,8 +132,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
         local: true,
         conversation_id: convo_id,
         in_reply_to_account_acct: nil,
-        content: %{"text/plain" => HtmlSanitizeEx.strip_tags(note.data["object"]["content"])},
-        spoiler_text: %{"text/plain" => HtmlSanitizeEx.strip_tags(note.data["object"]["summary"])}
+        content: %{"text/plain" => HtmlSanitizeEx.strip_tags(object_data["content"])},
+        spoiler_text: %{"text/plain" => HtmlSanitizeEx.strip_tags(object_data["summary"])}
       }
     }
 
@@ -202,10 +203,71 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
 
     status = StatusView.render("status.json", %{activity: activity})
 
-    actor = User.get_cached_by_ap_id(activity.actor)
-
     assert status.mentions ==
-             Enum.map([user, actor], fn u -> AccountView.render("mention.json", %{user: u}) end)
+             Enum.map([user], fn u -> AccountView.render("mention.json", %{user: u}) end)
+  end
+
+  test "create mentions from the 'to' field" do
+    %User{ap_id: recipient_ap_id} = insert(:user)
+    cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
+
+    object =
+      insert(:note, %{
+        data: %{
+          "to" => [recipient_ap_id],
+          "cc" => cc
+        }
+      })
+
+    activity =
+      insert(:note_activity, %{
+        note: object,
+        recipients: [recipient_ap_id | cc]
+      })
+
+    assert length(activity.recipients) == 3
+
+    %{mentions: [mention] = mentions} = StatusView.render("status.json", %{activity: activity})
+
+    assert length(mentions) == 1
+    assert mention.url == recipient_ap_id
+  end
+
+  test "create mentions from the 'tag' field" do
+    recipient = insert(:user)
+    cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
+
+    object =
+      insert(:note, %{
+        data: %{
+          "cc" => cc,
+          "tag" => [
+            %{
+              "href" => recipient.ap_id,
+              "name" => recipient.nickname,
+              "type" => "Mention"
+            },
+            %{
+              "href" => "https://example.com/search?tag=test",
+              "name" => "#test",
+              "type" => "Hashtag"
+            }
+          ]
+        }
+      })
+
+    activity =
+      insert(:note_activity, %{
+        note: object,
+        recipients: [recipient.ap_id | cc]
+      })
+
+    assert length(activity.recipients) == 3
+
+    %{mentions: [mention] = mentions} = StatusView.render("status.json", %{activity: activity})
+
+    assert length(mentions) == 1
+    assert mention.url == recipient.ap_id
   end
 
   test "attachments" do
index f66bf7834348e1dd3a2615564eaaa4ad7115d76e..3874e077bb6577819e0d6ba901e14f782adc1515 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.Metadata.Providers.RelMeTest do
   use Pleroma.DataCase
   import Pleroma.Factory
index 16ee02abb667c4173dcd3f5b9256b2c70e630fcb..a3a92ce5bf01d0e625dcc3e49de23f4e573e92ea 100644 (file)
@@ -38,22 +38,23 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
 
   test "a note activity" do
     note_activity = insert(:note_activity)
+    object_data = Object.normalize(note_activity).data
 
     user = User.get_cached_by_ap_id(note_activity.data["actor"])
 
     expected = """
     <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
     <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
-    <id>#{note_activity.data["object"]["id"]}</id>
+    <id>#{object_data["id"]}</id>
     <title>New note by #{user.nickname}</title>
-    <content type="html">#{note_activity.data["object"]["content"]}</content>
-    <published>#{note_activity.data["object"]["published"]}</published>
-    <updated>#{note_activity.data["object"]["published"]}</updated>
+    <content type="html">#{object_data["content"]}</content>
+    <published>#{object_data["published"]}</published>
+    <updated>#{object_data["published"]}</updated>
     <ostatus:conversation ref="#{note_activity.data["context"]}">#{note_activity.data["context"]}</ostatus:conversation>
     <link ref="#{note_activity.data["context"]}" rel="ostatus:conversation" />
-    <summary>#{note_activity.data["object"]["summary"]}</summary>
-    <link type="application/atom+xml" href="#{note_activity.data["object"]["id"]}" rel="self" />
-    <link type="text/html" href="#{note_activity.data["object"]["id"]}" rel="alternate" />
+    <summary>#{object_data["summary"]}</summary>
+    <link type="application/atom+xml" href="#{object_data["id"]}" rel="self" />
+    <link type="text/html" href="#{object_data["id"]}" rel="alternate" />
     <category term="2hu"/>
     <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
     <link name="2hu" rel="emoji" href="corndog.png" />
@@ -106,7 +107,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
   test "an announce activity" do
     note = insert(:note_activity)
     user = insert(:user)
-    object = Object.get_cached_by_ap_id(note.data["object"]["id"])
+    object = Object.normalize(note)
 
     {:ok, announce, _object} = ActivityPub.announce(user, object)
 
@@ -125,7 +126,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
     <activity:verb>http://activitystrea.ms/schema/1.0/share</activity:verb>
     <id>#{announce.data["id"]}</id>
     <title>#{user.nickname} repeated a notice</title>
-    <content type="html">RT #{note.data["object"]["content"]}</content>
+    <content type="html">RT #{object.data["content"]}</content>
     <published>#{announce.data["published"]}</published>
     <updated>#{announce.data["published"]}</updated>
     <ostatus:conversation ref="#{announce.data["context"]}">#{announce.data["context"]}</ostatus:conversation>
index ca6e6133952b36d1ba0c3f366d05487733087bf2..cd0447af7e4787826db9455a23ca31a53d833269 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.OStatus.DeleteHandlingTest do
   use Pleroma.DataCase
 
@@ -17,8 +21,9 @@ defmodule Pleroma.Web.OStatus.DeleteHandlingTest do
     test "it removes the mentioned activity" do
       note = insert(:note_activity)
       second_note = insert(:note_activity)
+      object = Object.normalize(note)
+      second_object = Object.normalize(second_note)
       user = insert(:user)
-      object = Object.get_by_ap_id(note.data["object"]["id"])
 
       {:ok, like, _object} = Pleroma.Web.ActivityPub.ActivityPub.like(user, object)
 
@@ -26,16 +31,16 @@ defmodule Pleroma.Web.OStatus.DeleteHandlingTest do
         File.read!("test/fixtures/delete.xml")
         |> String.replace(
           "tag:mastodon.sdf.org,2017-06-10:objectId=310513:objectType=Status",
-          note.data["object"]["id"]
+          object.data["id"]
         )
 
       {:ok, [delete]} = OStatus.handle_incoming(incoming)
 
       refute Activity.get_by_id(note.id)
       refute Activity.get_by_id(like.id)
-      assert Object.get_by_ap_id(note.data["object"]["id"]).data["type"] == "Tombstone"
+      assert Object.get_by_ap_id(object.data["id"]).data["type"] == "Tombstone"
       assert Activity.get_by_id(second_note.id)
-      assert Object.get_by_ap_id(second_note.data["object"]["id"])
+      assert Object.get_by_ap_id(second_object.data["id"])
 
       assert delete.data["type"] == "Delete"
     end
index eae44dba5125e734841e96a8f0f865d369c58a74..3dd8c6491fc5fb9a473bfc43a351a3d853572111 100644 (file)
@@ -72,6 +72,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
 
   test "gets a feed", %{conn: conn} do
     note_activity = insert(:note_activity)
+    object = Object.normalize(note_activity)
     user = User.get_cached_by_ap_id(note_activity.data["actor"])
 
     conn =
@@ -79,7 +80,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
       |> put_req_header("content-type", "application/atom+xml")
       |> get("/users/#{user.nickname}/feed.atom")
 
-    assert response(conn, 200) =~ note_activity.data["object"]["content"]
+    assert response(conn, 200) =~ object.data["content"]
   end
 
   test "returns 404 for a missing feed", %{conn: conn} do
@@ -93,8 +94,9 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
 
   test "gets an object", %{conn: conn} do
     note_activity = insert(:note_activity)
+    object = Object.normalize(note_activity)
     user = User.get_cached_by_ap_id(note_activity.data["actor"])
-    [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["object"]["id"]))
+    [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
     url = "/objects/#{uuid}"
 
     conn =
@@ -113,7 +115,8 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
 
   test "404s on private objects", %{conn: conn} do
     note_activity = insert(:direct_note_activity)
-    [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["object"]["id"]))
+    object = Object.normalize(note_activity)
+    [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
 
     conn
     |> get("/objects/#{uuid}")
@@ -138,8 +141,8 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
 
   test "404s on deleted objects", %{conn: conn} do
     note_activity = insert(:note_activity)
-    [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["object"]["id"]))
-    object = Object.get_by_ap_id(note_activity.data["object"]["id"])
+    object = Object.normalize(note_activity)
+    [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
 
     conn
     |> put_req_header("accept", "application/xml")
index acce330080dc394b05f81fe5785d49105ebaf672..4e8f3a0fc64cf689455c81430671cc413e57a16b 100644 (file)
@@ -30,7 +30,7 @@ defmodule Pleroma.Web.OStatusTest do
   test "handle incoming note - GS, Salmon" do
     incoming = File.read!("test/fixtures/incoming_note_activity.xml")
     {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity.data["object"])
+    object = Object.normalize(activity)
 
     user = User.get_cached_by_ap_id(activity.data["actor"])
     assert user.info.note_count == 1
@@ -53,7 +53,7 @@ defmodule Pleroma.Web.OStatusTest do
   test "handle incoming notes - GS, subscription" do
     incoming = File.read!("test/fixtures/ostatus_incoming_post.xml")
     {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity.data["object"])
+    object = Object.normalize(activity)
 
     assert activity.data["type"] == "Create"
     assert object.data["type"] == "Note"
@@ -67,7 +67,7 @@ defmodule Pleroma.Web.OStatusTest do
   test "handle incoming notes with attachments - GS, subscription" do
     incoming = File.read!("test/fixtures/incoming_websub_gnusocial_attachments.xml")
     {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity.data["object"])
+    object = Object.normalize(activity)
 
     assert activity.data["type"] == "Create"
     assert object.data["type"] == "Note"
@@ -80,7 +80,7 @@ defmodule Pleroma.Web.OStatusTest do
   test "handle incoming notes with tags" do
     incoming = File.read!("test/fixtures/ostatus_incoming_post_tag.xml")
     {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity.data["object"])
+    object = Object.normalize(activity)
 
     assert object.data["tag"] == ["nsfw"]
     assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
@@ -97,7 +97,7 @@ defmodule Pleroma.Web.OStatusTest do
 
     incoming = File.read!("test/fixtures/incoming_reply_mastodon.xml")
     {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity.data["object"])
+    object = Object.normalize(activity)
 
     assert activity.data["type"] == "Create"
     assert object.data["type"] == "Note"
@@ -109,7 +109,7 @@ defmodule Pleroma.Web.OStatusTest do
   test "handle incoming notes - Mastodon, with CW" do
     incoming = File.read!("test/fixtures/mastodon-note-cw.xml")
     {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity.data["object"])
+    object = Object.normalize(activity)
 
     assert activity.data["type"] == "Create"
     assert object.data["type"] == "Note"
@@ -121,7 +121,7 @@ defmodule Pleroma.Web.OStatusTest do
   test "handle incoming unlisted messages, put public into cc" do
     incoming = File.read!("test/fixtures/mastodon-note-unlisted.xml")
     {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity.data["object"])
+    object = Object.normalize(activity)
 
     refute "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
     assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["cc"]
@@ -132,7 +132,7 @@ defmodule Pleroma.Web.OStatusTest do
   test "handle incoming retweets - Mastodon, with CW" do
     incoming = File.read!("test/fixtures/cw_retweet.xml")
     {:ok, [[_activity, retweeted_activity]]} = OStatus.handle_incoming(incoming)
-    retweeted_object = Object.normalize(retweeted_activity.data["object"])
+    retweeted_object = Object.normalize(retweeted_activity)
 
     assert retweeted_object.data["summary"] == "Hey."
   end
@@ -140,7 +140,7 @@ defmodule Pleroma.Web.OStatusTest do
   test "handle incoming notes - GS, subscription, reply" do
     incoming = File.read!("test/fixtures/ostatus_incoming_reply.xml")
     {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity.data["object"])
+    object = Object.normalize(activity)
 
     assert activity.data["type"] == "Create"
     assert object.data["type"] == "Note"
@@ -166,7 +166,7 @@ defmodule Pleroma.Web.OStatusTest do
     refute activity.local
 
     retweeted_activity = Activity.get_by_id(retweeted_activity.id)
-    retweeted_object = Object.normalize(retweeted_activity.data["object"])
+    retweeted_object = Object.normalize(retweeted_activity)
     assert retweeted_activity.data["type"] == "Create"
     assert retweeted_activity.data["actor"] == "https://pleroma.soykaf.com/users/lain"
     refute retweeted_activity.local
@@ -178,18 +178,19 @@ defmodule Pleroma.Web.OStatusTest do
   test "handle incoming retweets - GS, subscription - local message" do
     incoming = File.read!("test/fixtures/share-gs-local.xml")
     note_activity = insert(:note_activity)
+    object = Object.normalize(note_activity)
     user = User.get_cached_by_ap_id(note_activity.data["actor"])
 
     incoming =
       incoming
-      |> String.replace("LOCAL_ID", note_activity.data["object"]["id"])
+      |> String.replace("LOCAL_ID", object.data["id"])
       |> String.replace("LOCAL_USER", user.ap_id)
 
     {:ok, [[activity, retweeted_activity]]} = OStatus.handle_incoming(incoming)
 
     assert activity.data["type"] == "Announce"
     assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
-    assert activity.data["object"] == retweeted_activity.data["object"]["id"]
+    assert activity.data["object"] == object.data["id"]
     assert user.ap_id in activity.data["to"]
     refute activity.local
 
@@ -204,7 +205,7 @@ defmodule Pleroma.Web.OStatusTest do
   test "handle incoming retweets - Mastodon, salmon" do
     incoming = File.read!("test/fixtures/share.xml")
     {:ok, [[activity, retweeted_activity]]} = OStatus.handle_incoming(incoming)
-    retweeted_object = Object.normalize(retweeted_activity.data["object"])
+    retweeted_object = Object.normalize(retweeted_activity)
 
     assert activity.data["type"] == "Announce"
     assert activity.data["actor"] == "https://mastodon.social/users/lambadalambda"
@@ -253,16 +254,17 @@ defmodule Pleroma.Web.OStatusTest do
 
   test "handle incoming favorites with locally available object - GS, websub" do
     note_activity = insert(:note_activity)
+    object = Object.normalize(note_activity)
 
     incoming =
       File.read!("test/fixtures/favorite_with_local_note.xml")
-      |> String.replace("localid", note_activity.data["object"]["id"])
+      |> String.replace("localid", object.data["id"])
 
     {:ok, [[activity, favorited_activity]]} = OStatus.handle_incoming(incoming)
 
     assert activity.data["type"] == "Like"
     assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
-    assert activity.data["object"] == favorited_activity.data["object"]["id"]
+    assert activity.data["object"] == object.data["id"]
     refute activity.local
     assert note_activity.id == favorited_activity.id
     assert favorited_activity.local
@@ -274,7 +276,7 @@ defmodule Pleroma.Web.OStatusTest do
                  [] do
     incoming = File.read!("test/fixtures/incoming_note_activity_answer.xml")
     {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity.data["object"], false)
+    object = Object.normalize(activity, false)
 
     assert activity.data["type"] == "Create"
     assert object.data["type"] == "Note"
@@ -300,7 +302,7 @@ defmodule Pleroma.Web.OStatusTest do
     with_mock Pleroma.Web.Federator,
       allowed_incoming_reply_depth?: fn _ -> false end do
       {:ok, [activity]} = OStatus.handle_incoming(incoming)
-      object = Object.normalize(activity.data["object"], false)
+      object = Object.normalize(activity, false)
 
       refute called(OStatus.fetch_activity_from_url(object.data["inReplyTo"], :_))
     end
@@ -337,13 +339,14 @@ defmodule Pleroma.Web.OStatusTest do
              "undo:tag:social.heldscal.la,2017-05-07:subscription:23211:person:44803:2017-05-07T09:54:48+00:00"
 
     assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
-    assert is_map(activity.data["object"])
-    assert activity.data["object"]["type"] == "Follow"
-    assert activity.data["object"]["object"] == "https://pawoo.net/users/pekorino"
+    embedded_object = activity.data["object"]
+    assert is_map(embedded_object)
+    assert embedded_object["type"] == "Follow"
+    assert embedded_object["object"] == "https://pawoo.net/users/pekorino"
     refute activity.local
 
     follower = User.get_cached_by_ap_id(activity.data["actor"])
-    followed = User.get_cached_by_ap_id(activity.data["object"]["object"])
+    followed = User.get_cached_by_ap_id(embedded_object["object"])
 
     refute User.following?(follower, followed)
   end
@@ -560,8 +563,7 @@ defmodule Pleroma.Web.OStatusTest do
 
     test "Article objects are not representable" do
       note_activity = insert(:note_activity)
-
-      note_object = Object.normalize(note_activity.data["object"])
+      note_object = Object.normalize(note_activity)
 
       note_data =
         note_object.data
index 5188f4de18cb68d92e0f78f17a80edf1a225b3e8..85515c4329c9a13a26eaa4033104796f8e1791b0 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.RelMeTest do
   use ExUnit.Case, async: true
 
index c8f442b05e4dd98d126e5a30a3102c76e0d5b404..92198f3d9b91abeb8851ae76cc4bbd8b4b136a2d 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.RichMedia.HelpersTest do
   use Pleroma.DataCase
 
index bc48341cad608572bc9a6a3f8871f17eda4972f8..19c19e895eb708ec6d8f8ee50009d70b49b61a46 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.RichMedia.ParserTest do
   use ExUnit.Case, async: true
 
index 6b9da82045ac5546544ae81ebc2272d31d5d95e5..3a7246ea8321ac99440fe7c252ffec10269cce4c 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do
   use Pleroma.Web.ConnCase
 
index 3760b44b6c2688c67236a3220e0469cd591c43be..7ec0e101d52b4ba771a384f40f3792a6a6a285e1 100644 (file)
@@ -929,7 +929,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
 
     test "with credentials", %{conn: conn, user: current_user} do
       note_activity = insert(:note_activity)
-      object = Object.get_by_ap_id(note_activity.data["object"]["id"])
+      object = Object.normalize(note_activity)
       ActivityPub.like(current_user, object)
 
       conn =
index 475531a097e58dfcde78ae32eb6474e7a9284f76..cbe83852e9bade09009cfc2407e0a594da4883d2 100644 (file)
@@ -46,7 +46,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
     }
 
     {:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input)
-    object = Object.normalize(activity.data["object"])
+    object = Object.normalize(activity)
 
     expected_text =
       "Hello again, <span class='h-card'><a data-user='#{mentioned_user.id}' class='u-url mention' href='shp'>@<span>shp</span></a></span>.&lt;script&gt;&lt;/script&gt;<br>This is on another :firefox: line. <a class='hashtag' data-tag='2hu' href='http://localhost:4001/tag/2hu' rel='tag'>#2hu</a> <a class='hashtag' data-tag='epic' href='http://localhost:4001/tag/epic' rel='tag'>#epic</a> <a class='hashtag' data-tag='phantasmagoric' href='http://localhost:4001/tag/phantasmagoric' rel='tag'>#phantasmagoric</a><br><a href=\"http://example.org/image.jpg\" class='attachment'>image.jpg</a>"
@@ -91,7 +91,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
     }
 
     {:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input)
-    object = Object.normalize(activity.data["object"])
+    object = Object.normalize(activity)
 
     input = %{
       "status" => "Here's your (you).",
@@ -99,7 +99,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
     }
 
     {:ok, reply = %Activity{}} = TwitterAPI.create_status(user, input)
-    reply_object = Object.normalize(reply.data["object"])
+    reply_object = Object.normalize(reply)
 
     assert get_in(reply.data, ["context"]) == get_in(activity.data, ["context"])
 
@@ -216,7 +216,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
     updated_activity = Activity.get_by_ap_id(note_activity.data["id"])
     assert ActivityView.render("activity.json", %{activity: updated_activity})["fave_num"] == 1
 
-    object = Object.normalize(note_activity.data["object"])
+    object = Object.normalize(note_activity)
 
     assert object.data["like_count"] == 1
 
@@ -224,7 +224,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
 
     {:ok, _status} = TwitterAPI.fav(other_user, note_activity.id)
 
-    object = Object.normalize(note_activity.data["object"])
+    object = Object.normalize(note_activity)
 
     assert object.data["like_count"] == 2
 
@@ -235,7 +235,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
   test "it unfavorites a status, returns the updated activity" do
     user = insert(:user)
     note_activity = insert(:note_activity)
-    object = Object.get_by_ap_id(note_activity.data["object"]["id"])
+    object = Object.normalize(note_activity)
 
     {:ok, _like_activity, _object} = ActivityPub.like(user, object)
     updated_activity = Activity.get_by_ap_id(note_activity.data["id"])
index cab9e5d904f79d0725e4c71fdeee467f2878f0ea..21324399fb3fc5b12863dae94ff152994b5ff796 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
   use Pleroma.Web.ConnCase
 
index 43bd77f782965db1bb5a16424c5f4e5140a976e8..56d861efbe8bcbc98431d8968e1603d3bc4cca21 100644 (file)
@@ -126,7 +126,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
     other_user = insert(:user, %{nickname: "shp"})
 
     {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"})
-    object = Object.normalize(activity.data["object"])
+    object = Object.normalize(activity)
 
     result = ActivityView.render("activity.json", activity: activity)
 
@@ -177,7 +177,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
     user = insert(:user)
     other_user = insert(:user, %{nickname: "shp"})
     {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"})
-    object = Object.normalize(activity.data["object"])
+    object = Object.normalize(activity)
 
     convo_id = Utils.context_to_conversation_id(object.data["context"])
 
@@ -351,7 +351,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
       "is_post_verb" => false,
       "statusnet_html" => "deleted notice {{tag",
       "text" => "deleted notice {{tag",
-      "uri" => delete.data["object"],
+      "uri" => Object.normalize(delete).data["id"],
       "user" => UserView.render("show.json", user: user)
     }
 
index 335c95b18ba14e8fa10433aa220b0b14c5c17d86..0578b4b8e5dbbc25fda09547ccaec1ba9da3fa7b 100644 (file)
@@ -104,5 +104,16 @@ defmodule Pleroma.Web.WebFingerTest do
 
       assert template == "http://status.alpicola.com/main/xrd?uri={uri}"
     end
+
+    test "it works with idna domains as nickname" do
+      nickname = "lain@" <> to_string(:idna.encode("zetsubou.みんな"))
+
+      {:ok, _data} = WebFinger.finger(nickname)
+    end
+
+    test "it works with idna domains as link" do
+      ap_id = "https://" <> to_string(:idna.encode("zetsubou.みんな")) <> "/users/lain"
+      {:ok, _data} = WebFinger.finger(ap_id)
+    end
   end
 end