Merge branch 'issue/1023' into 'develop'
authorlain <lain@soykaf.club>
Wed, 29 Jul 2020 13:45:15 +0000 (13:45 +0000)
committerlain <lain@soykaf.club>
Wed, 29 Jul 2020 13:45:15 +0000 (13:45 +0000)
[#1023] added generated  `pleroma.env`

See merge request pleroma/pleroma!2763

94 files changed:
CHANGELOG.md
config/config.exs
config/description.exs
config/test.exs
docs/API/admin_api.md
docs/configuration/cheatsheet.md
lib/mix/pleroma.ex
lib/pleroma/application_requirements.ex
lib/pleroma/config/config_db.ex
lib/pleroma/config/deprecation_warnings.ex
lib/pleroma/config/helpers.ex [new file with mode: 0644]
lib/pleroma/emails/admin_email.ex
lib/pleroma/emails/user_email.ex
lib/pleroma/formatter.ex
lib/pleroma/gun/connection_pool.ex
lib/pleroma/gun/connection_pool/worker.ex
lib/pleroma/moderation_log.ex
lib/pleroma/object/fetcher.ex
lib/pleroma/plugs/frontend_static.ex [new file with mode: 0644]
lib/pleroma/plugs/instance_static.ex
lib/pleroma/reverse_proxy/client/tesla.ex
lib/pleroma/reverse_proxy/reverse_proxy.ex
lib/pleroma/user.ex
lib/pleroma/user/query.ex
lib/pleroma/user/welcome_email.ex [new file with mode: 0644]
lib/pleroma/user/welcome_message.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex
lib/pleroma/web/activity_pub/object_validator.ex
lib/pleroma/web/activity_pub/pipeline.ex
lib/pleroma/web/activity_pub/transmogrifier.ex
lib/pleroma/web/activity_pub/utils.ex
lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
lib/pleroma/web/admin_api/views/account_view.ex
lib/pleroma/web/api_spec/operations/chat_operation.ex
lib/pleroma/web/api_spec/operations/domain_block_operation.ex
lib/pleroma/web/chat_channel.ex
lib/pleroma/web/endpoint.ex
lib/pleroma/web/feed/user_controller.ex
lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex
lib/pleroma/web/mastodon_api/controllers/search_controller.ex
lib/pleroma/web/mastodon_api/views/account_view.ex
lib/pleroma/web/mastodon_api/views/conversation_view.ex
lib/pleroma/web/mastodon_api/views/instance_view.ex
lib/pleroma/web/mastodon_api/views/status_view.ex
lib/pleroma/web/oauth/oauth_controller.ex
lib/pleroma/web/pleroma_api/controllers/chat_controller.ex
lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex
lib/pleroma/web/pleroma_api/views/chat_view.ex
lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex
lib/pleroma/web/rich_media/helpers.ex
lib/pleroma/web/router.ex
lib/pleroma/web/twitter_api/twitter_api.ex
lib/pleroma/web/views/masto_fe_view.ex
mix.exs
mix.lock
priv/repo/migrations/20200712234852_add_approval_fields_to_users.exs [new file with mode: 0644]
priv/repo/migrations/20200716195806_autolinker_to_linkify.exs [new file with mode: 0644]
priv/repo/migrations/20200722185515_fix_malformed_formatter_config.exs [new file with mode: 0644]
priv/repo/migrations/20200724133313_move_welcome_settings.exs [new file with mode: 0644]
test/application_requirements_test.exs
test/emails/admin_email_test.exs
test/formatter_test.exs
test/migrations/20200716195806_autolinker_to_linkify_test.exs [new file with mode: 0644]
test/migrations/20200722185515_fix_malformed_formatter_config_test.exs [new file with mode: 0644]
test/migrations/20200724133313_move_welcome_settings_test.exs [new file with mode: 0644]
test/plugs/frontend_static_test.exs [new file with mode: 0644]
test/plugs/instance_static_test.exs
test/support/helpers.ex
test/tasks/config_test.exs
test/user/welcome_email_test.exs [new file with mode: 0644]
test/user/welcome_message_test.exs [new file with mode: 0644]
test/user_test.exs
test/web/activity_pub/activity_pub_test.exs
test/web/activity_pub/mrf/ensure_re_prepended_test.exs
test/web/activity_pub/pipeline_test.exs
test/web/activity_pub/side_effects_test.exs
test/web/activity_pub/transmogrifier_test.exs
test/web/activity_pub/utils_test.exs
test/web/admin_api/controllers/admin_api_controller_test.exs
test/web/admin_api/search_test.exs
test/web/admin_api/views/report_view_test.exs
test/web/common_api/common_api_test.exs
test/web/feed/user_controller_test.exs
test/web/mastodon_api/controllers/account_controller_test.exs
test/web/mastodon_api/controllers/domain_block_controller_test.exs
test/web/mastodon_api/controllers/instance_controller_test.exs
test/web/mastodon_api/views/account_view_test.exs
test/web/mastodon_api/views/status_view_test.exs
test/web/oauth/oauth_controller_test.exs
test/web/pleroma_api/controllers/chat_controller_test.exs
test/web/pleroma_api/controllers/emoji_pack_controller_test.exs
test/web/pleroma_api/views/chat_view_test.exs
test/web/twitter_api/twitter_api_test.exs

index 08027007339d1cb6e6fd0cf76949e3d3018964f9..7d5256600df8986d5e5995d8667feddb28f0341b 100644 (file)
@@ -7,12 +7,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ### Changed
 - **Breaking:** Elixir >=1.9 is now required (was >= 1.8)
+- **Breaking:** Configuration: `:auto_linker, :opts` moved to `:pleroma, Pleroma.Formatter`. Old config namespace is deprecated.
 - In Conversations, return only direct messages as `last_status`
 - Using the `only_media` filter on timelines will now exclude reblog media
 - MFR policy to set global expiration for all local Create activities
 - OGP rich media parser merged with TwitterCard
 - Configuration: `:instance, rewrite_policy` moved to `:mrf, policies`, `:instance, :mrf_transparency` moved to `:mrf, :transparency`, `:instance, :mrf_transparency_exclusions` moved to `:mrf, :transparency_exclusions`. Old config namespace is deprecated.
 - Configuration: `:media_proxy, whitelist` format changed to host with scheme (e.g. `http://example.com` instead of `example.com`). Domain format is deprecated.
+- **Breaking:** Configuration: `:instance, welcome_user_nickname` moved to `:welcome, :direct_message, :sender_nickname`, `:instance, :welcome_message` moved to `:welcome, :direct_message, :message`. Old config namespace is deprecated.
 
 <details>
   <summary>API Changes</summary>
@@ -30,6 +32,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - **Breaking:** Notification Settings API option for hiding push notification
   contents has been renamed to `hide_notification_contents`
 - Mastodon API: Added `pleroma.metadata.post_formats` to /api/v1/instance
+- Mastodon API (legacy): Allow query parameters for `/api/v1/domain_blocks`, e.g. `/api/v1/domain_blocks?domain=badposters.zone`
 </details>
 
 <details>
@@ -64,6 +67,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Support pagination in emoji packs API (for packs and for files in pack)
 - Support for viewing instances favicons next to posts and accounts
 - Added Pleroma.Upload.Filter.Exiftool as an alternate EXIF stripping mechanism targeting GPS/location metadata.
+- "By approval" registrations mode.
+- Configuration: Added `:welcome` settings for the welcome message to newly registered users.
 
 <details>
   <summary>API Changes</summary>
@@ -93,6 +98,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Admin API: fix `GET /api/pleroma/admin/users/:nickname/credentials` returning 404 when getting the credentials of a remote user while `:instance, :limit_to_local_content` is set to `:unauthenticated`
 - Fix CSP policy generation to include remote Captcha services
 - Fix edge case where MediaProxy truncates media, usually caused when Caddy is serving content for the other Federated instance.
+- Emoji Packs could not be listed when instance was set to `public: false`
 
 ## [Unreleased (patch)]
 
index 2d3f35e70253cc3cf654ad4e792a8fecbb0f07c8..4b91a58b746de9830726a80d8a8524e8a997852e 100644 (file)
@@ -205,6 +205,7 @@ config :pleroma, :instance,
   registrations_open: true,
   invites_enabled: false,
   account_activation_required: false,
+  account_approval_required: false,
   federating: true,
   federation_incoming_replies_max_depth: 100,
   federation_reachability_timeout_days: 7,
@@ -225,8 +226,6 @@ config :pleroma, :instance,
   autofollowed_nicknames: [],
   max_pinned_statuses: 1,
   attachment_links: false,
-  welcome_user_nickname: nil,
-  welcome_message: nil,
   max_report_comment_size: 1000,
   safe_dm_mentions: false,
   healthcheck: false,
@@ -239,6 +238,7 @@ config :pleroma, :instance,
   max_remote_account_fields: 20,
   account_field_name_length: 512,
   account_field_value_length: 2048,
+  registration_reason_length: 500,
   external_user_synchronization: true,
   extended_nickname_format: true,
   cleanup_attachments: false,
@@ -254,6 +254,20 @@ config :pleroma, :instance,
     ]
   ]
 
+config :pleroma, :welcome,
+  direct_message: [
+    enabled: false,
+    sender_nickname: nil,
+    message: nil
+  ],
+  email: [
+    enabled: false,
+    sender: nil,
+    subject: "Welcome to <%= instance_name %>",
+    html: "Welcome to <%= instance_name %>",
+    text: "Welcome to <%= instance_name %>"
+  ]
+
 config :pleroma, :feed,
   post_title: %{
     max_length: 100,
@@ -527,16 +541,14 @@ config :pleroma, :workers,
     federator_outgoing: 5
   ]
 
-config :auto_linker,
-  opts: [
-    extra: true,
-    # TODO: Set to :no_scheme when it works properly
-    validate_tld: true,
-    class: false,
-    strip_prefix: false,
-    new_window: false,
-    rel: "ugc"
-  ]
+config :pleroma, Pleroma.Formatter,
+  class: false,
+  rel: "ugc",
+  new_window: false,
+  truncate: false,
+  strip_prefix: false,
+  extra: true,
+  validate_tld: :no_scheme
 
 config :pleroma, :ldap,
   enabled: System.get_env("LDAP_ENABLED") == "true",
@@ -635,6 +647,16 @@ config :pleroma, Pleroma.Plugs.RemoteIp, enabled: true
 
 config :pleroma, :static_fe, enabled: false
 
+# Example of frontend configuration
+# This example will make us serve the primary frontend from the
+# frontends directory within your `:pleroma, :instance, static_dir`.
+# e.g., instance/static/frontends/pleroma/develop/
+#
+# With no frontend configuration, the bundled files from the `static` directory will
+# be used.
+#
+# config :pleroma, :frontends, primary: %{"name" => "pleroma", "ref" => "develop"}
+
 config :pleroma, :web_cache_ttl,
   activity_pub: nil,
   activity_pub_question: 30_000
index f1c6773f1962b224fe336ce20ae0c7436d50410d..30a503696c2271a14738316d837e2f56a38610a6 100644 (file)
@@ -661,6 +661,11 @@ config :pleroma, :config_description, [
         type: :boolean,
         description: "Require users to confirm their emails before signing in"
       },
+      %{
+        key: :account_approval_required,
+        type: :boolean,
+        description: "Require users to be manually approved by an admin before signing in"
+      },
       %{
         key: :federating,
         type: :boolean,
@@ -778,23 +783,6 @@ config :pleroma, :config_description, [
         type: :boolean,
         description: "Enable to automatically add attachment link text to statuses"
       },
-      %{
-        key: :welcome_message,
-        type: :string,
-        description:
-          "A message that will be sent to a newly registered users as a direct message",
-        suggestions: [
-          "Hi, @username! Welcome on board!"
-        ]
-      },
-      %{
-        key: :welcome_user_nickname,
-        type: :string,
-        description: "The nickname of the local user that sends the welcome message",
-        suggestions: [
-          "lain"
-        ]
-      },
       %{
         key: :max_report_comment_size,
         type: :integer,
@@ -891,6 +879,14 @@ config :pleroma, :config_description, [
           2048
         ]
       },
+      %{
+        key: :registration_reason_length,
+        type: :integer,
+        description: "Maximum registration reason length. Default: 500.",
+        suggestions: [
+          500
+        ]
+      },
       %{
         key: :external_user_synchronization,
         type: :boolean,
@@ -962,6 +958,84 @@ config :pleroma, :config_description, [
       }
     ]
   },
+  %{
+    group: :welcome,
+    type: :group,
+    description: "Welcome messages settings",
+    children: [
+      %{
+        group: :direct_message,
+        type: :group,
+        descpiption: "Direct message settings",
+        children: [
+          %{
+            key: :enabled,
+            type: :boolean,
+            description: "Enables sends direct message for new user after registration"
+          },
+          %{
+            key: :message,
+            type: :string,
+            description:
+              "A message that will be sent to a newly registered users as a direct message",
+            suggestions: [
+              "Hi, @username! Welcome on board!"
+            ]
+          },
+          %{
+            key: :sender_nickname,
+            type: :string,
+            description: "The nickname of the local user that sends the welcome message",
+            suggestions: [
+              "lain"
+            ]
+          }
+        ]
+      },
+      %{
+        group: :email,
+        type: :group,
+        descpiption: "Email message settings",
+        children: [
+          %{
+            key: :enabled,
+            type: :boolean,
+            description: "Enables sends direct message for new user after registration"
+          },
+          %{
+            key: :sender,
+            type: [:string, :tuple],
+            description:
+              "The email address or tuple with `{nickname, email}` that will use as sender to the welcome email.",
+            suggestions: [
+              {"Pleroma App", "welcome@pleroma.app"}
+            ]
+          },
+          %{
+            key: :subject,
+            type: :string,
+            description:
+              "The subject of welcome email. Can be use EEX template with `user` and `instance_name` variables.",
+            suggestions: ["Welcome to <%= instance_name%>"]
+          },
+          %{
+            key: :html,
+            type: :string,
+            description:
+              "The html content of welcome email. Can be use EEX template with `user` and `instance_name` variables.",
+            suggestions: ["<h1>Hello <%= user.name%>. Welcome to <%= instance_name%></h1>"]
+          },
+          %{
+            key: :text,
+            type: :string,
+            description:
+              "The text content of welcome email. Can be use EEX template with `user` and `instance_name` variables.",
+            suggestions: ["Hello <%= user.name%>. \n Welcome to <%= instance_name%>\n"]
+          }
+        ]
+      }
+    ]
+  },
   %{
     group: :logger,
     type: :group,
@@ -1426,6 +1500,7 @@ config :pleroma, :config_description, [
     group: :pleroma,
     key: :mrf_simple,
     tab: :mrf,
+    related_policy: "Pleroma.Web.ActivityPub.MRF.SimplePolicy",
     label: "MRF Simple",
     type: :group,
     description: "Simple ingress policies",
@@ -1492,6 +1567,7 @@ config :pleroma, :config_description, [
     group: :pleroma,
     key: :mrf_activity_expiration,
     tab: :mrf,
+    related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy",
     label: "MRF Activity Expiration Policy",
     type: :group,
     description: "Adds automatic expiration to all local activities",
@@ -1508,6 +1584,7 @@ config :pleroma, :config_description, [
     group: :pleroma,
     key: :mrf_subchain,
     tab: :mrf,
+    related_policy: "Pleroma.Web.ActivityPub.MRF.SubchainPolicy",
     label: "MRF Subchain",
     type: :group,
     description:
@@ -1530,6 +1607,7 @@ config :pleroma, :config_description, [
     group: :pleroma,
     key: :mrf_rejectnonpublic,
     tab: :mrf,
+    related_policy: "Pleroma.Web.ActivityPub.MRF.RejectNonPublic",
     description: "RejectNonPublic drops posts with non-public visibility settings.",
     label: "MRF Reject Non Public",
     type: :group,
@@ -1551,6 +1629,7 @@ config :pleroma, :config_description, [
     group: :pleroma,
     key: :mrf_hellthread,
     tab: :mrf,
+    related_policy: "Pleroma.Web.ActivityPub.MRF.HellthreadPolicy",
     label: "MRF Hellthread",
     type: :group,
     description: "Block messages with excessive user mentions",
@@ -1576,6 +1655,7 @@ config :pleroma, :config_description, [
     group: :pleroma,
     key: :mrf_keyword,
     tab: :mrf,
+    related_policy: "Pleroma.Web.ActivityPub.MRF.KeywordPolicy",
     label: "MRF Keyword",
     type: :group,
     description: "Reject or Word-Replace messages with a keyword or regex",
@@ -1607,6 +1687,7 @@ config :pleroma, :config_description, [
     group: :pleroma,
     key: :mrf_mention,
     tab: :mrf,
+    related_policy: "Pleroma.Web.ActivityPub.MRF.MentionPolicy",
     label: "MRF Mention",
     type: :group,
     description: "Block messages which mention a specific user",
@@ -1623,6 +1704,7 @@ config :pleroma, :config_description, [
     group: :pleroma,
     key: :mrf_vocabulary,
     tab: :mrf,
+    related_policy: "Pleroma.Web.ActivityPub.MRF.VocabularyPolicy",
     label: "MRF Vocabulary",
     type: :group,
     description: "Filter messages which belong to certain activity vocabularies",
@@ -1646,6 +1728,8 @@ config :pleroma, :config_description, [
   # %{
   #   group: :pleroma,
   #   key: :mrf_user_allowlist,
+  #   tab: :mrf,
+  #   related_policy: "Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy",
   #   type: :map,
   #   description:
   #     "The keys in this section are the domain names that the policy should apply to." <>
@@ -2216,45 +2300,53 @@ config :pleroma, :config_description, [
     ]
   },
   %{
-    group: :auto_linker,
-    key: :opts,
+    group: :pleroma,
+    key: Pleroma.Formatter,
     label: "Auto Linker",
     type: :group,
-    description: "Configuration for the auto_linker library",
+    description:
+      "Configuration for Pleroma's link formatter which parses mentions, hashtags, and URLs.",
     children: [
       %{
         key: :class,
-        type: [:string, false],
+        type: [:string, :boolean],
         description: "Specify the class to be added to the generated link. Disable to clear.",
         suggestions: ["auto-linker", false]
       },
       %{
         key: :rel,
-        type: [:string, false],
+        type: [:string, :boolean],
         description: "Override the rel attribute. Disable to clear.",
         suggestions: ["ugc", "noopener noreferrer", false]
       },
       %{
         key: :new_window,
         type: :boolean,
-        description: "Link URLs will open in new window/tab"
+        description: "Link URLs will open in a new window/tab."
       },
       %{
         key: :truncate,
-        type: [:integer, false],
+        type: [:integer, :boolean],
         description:
-          "Set to a number to truncate URLs longer then the number. Truncated URLs will end in `..`",
+          "Set to a number to truncate URLs longer than the number. Truncated URLs will end in `...`",
         suggestions: [15, false]
       },
       %{
         key: :strip_prefix,
         type: :boolean,
-        description: "Strip the scheme prefix"
+        description: "Strip the scheme prefix."
       },
       %{
         key: :extra,
         type: :boolean,
         description: "Link URLs with rarely used schemes (magnet, ipfs, irc, etc.)"
+      },
+      %{
+        key: :validate_tld,
+        type: [:atom, :boolean],
+        description:
+          "Set to false to disable TLD validation for URLs/emails. Can be set to :no_scheme to validate TLDs only for URLs without a scheme (e.g `example.com` will be validated, but `http://example.loki` won't)",
+        suggestions: [:no_scheme, true]
       }
     ]
   },
@@ -2902,8 +2994,9 @@ config :pleroma, :config_description, [
   },
   %{
     group: :pleroma,
-    tab: :mrf,
     key: :mrf_normalize_markup,
+    tab: :mrf,
+    related_policy: "Pleroma.Web.ActivityPub.MRF.NormalizeMarkup",
     label: "MRF Normalize Markup",
     description: "MRF NormalizeMarkup settings. Scrub configured hypertext markup.",
     type: :group,
@@ -3098,8 +3191,9 @@ config :pleroma, :config_description, [
   %{
     group: :pleroma,
     key: :mrf_object_age,
-    label: "MRF Object Age",
     tab: :mrf,
+    related_policy: "Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy",
+    label: "MRF Object Age",
     type: :group,
     description:
       "Rejects or delists posts based on their timestamp deviance from your server's clock.",
@@ -3400,5 +3494,30 @@ config :pleroma, :config_description, [
         suggestions: ["s3.eu-central-1.amazonaws.com"]
       }
     ]
+  },
+  %{
+    group: :pleroma,
+    key: :frontends,
+    type: :group,
+    description: "Installed frontends management",
+    children: [
+      %{
+        key: :primary,
+        type: :map,
+        description: "Primary frontend, the one that is served for all pages by default",
+        children: [
+          %{
+            key: "name",
+            type: :string,
+            description: "Name of the installed primary frontend"
+          },
+          %{
+            key: "ref",
+            type: :string,
+            description: "reference of the installed primary frontend to be used"
+          }
+        ]
+      }
+    ]
   }
 ]
index abcf793e5934138b3384d615c6881dfd0429745b..db0655e7377bdcb07f11b840e917b30a4844cd47 100644 (file)
@@ -118,6 +118,8 @@ config :pleroma, Pleroma.Uploaders.S3,
   streaming_enabled: true,
   public_endpoint: nil
 
+config :tzdata, :autoupdate, :disabled
+
 if File.exists?("./config/test.secret.exs") do
   import_config "test.secret.exs"
 else
index baf895d90923fe7ed33f55e299caec0256f60d99..4b143e4eec0d7c7df57bf2d599a0f3b596fa65c6 100644 (file)
@@ -19,6 +19,7 @@ Configuration options:
     - `local`: only local users
     - `external`: only external users
     - `active`: only active users
+    - `need_approval`: only unapproved users
     - `deactivated`: only deactivated users
     - `is_admin`: users with admin role
     - `is_moderator`: users with moderator role
@@ -46,7 +47,10 @@ Configuration options:
       "local": bool,
       "tags": array,
       "avatar": string,
-      "display_name": string
+      "display_name": string,
+      "confirmation_pending": bool,
+      "approval_pending": bool,
+      "registration_reason": string,
     },
     ...
   ]
@@ -242,6 +246,24 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
 }
 ```
 
+## `PATCH /api/pleroma/admin/users/approve`
+
+### Approve user
+
+- Params:
+  - `nicknames`: nicknames array
+- Response:
+
+```json
+{
+  users: [
+    {
+      // user object
+    }
+  ]
+}
+```
+
 ## `GET /api/pleroma/admin/users/:nickname_or_id`
 
 ### Retrive the details of a user
index 6c1babba32251c38e7dfaa5ad67895ff0988603c..d6a9276eeb6f7613b3c61f79c11da79ca3df156d 100644 (file)
@@ -33,6 +33,7 @@ To add configuration to your config file, you can copy it from the base config.
 * `registrations_open`: Enable registrations for anyone, invitations can be enabled when false.
 * `invites_enabled`: Enable user invitations for admins (depends on `registrations_open: false`).
 * `account_activation_required`: Require users to confirm their emails before signing in.
+* `account_approval_required`: Require users to be manually approved by an admin before signing in.
 * `federating`: Enable federation with other instances.
 * `federation_incoming_replies_max_depth`: Max. depth of reply-to activities fetching on incoming federation, to prevent out-of-memory situations while fetching very long threads. If set to `nil`, threads of any depth will be fetched. Lower this value if you experience out-of-memory crashes.
 * `federation_reachability_timeout_days`: Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.
@@ -46,8 +47,6 @@ To add configuration to your config file, you can copy it from the base config.
 * `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature.
 * `autofollowed_nicknames`: Set to nicknames of (local) users that every new user should automatically follow.
 * `attachment_links`: Set to true to enable automatically adding attachment link text to statuses.
-* `welcome_message`: A message that will be send to a newly registered users as a direct message.
-* `welcome_user_nickname`: The nickname of the local user that sends the welcome message.
 * `max_report_comment_size`: The maximum size of the report comment (Default: `1000`).
 * `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). Default: `false`.
 * `healthcheck`: If set to true, system data will be shown on ``/api/pleroma/healthcheck``.
@@ -60,9 +59,40 @@ To add configuration to your config file, you can copy it from the base config.
 * `max_remote_account_fields`: The maximum number of custom fields in the remote user profile (default: `20`).
 * `account_field_name_length`: An account field name maximum length (default: `512`).
 * `account_field_value_length`: An account field value maximum length (default: `2048`).
+* `registration_reason_length`: Maximum registration reason length (default: `500`).
 * `external_user_synchronization`: Enabling following/followers counters synchronization for external users.
 * `cleanup_attachments`: Remove attachments along with statuses. Does not affect duplicate files and attachments without status. Enabling this will increase load to database when deleting statuses on larger instances.
 
+## Welcome
+* `direct_message`: - welcome message sent as a direct message.
+  * `enabled`: Enables the send a direct message to a newly registered user. Defaults to `false`.
+  * `sender_nickname`: The nickname of the local user that sends the welcome message.
+  * `message`: A message that will be send to a newly registered users as a direct message.
+* `email`: - welcome message sent as a email.
+  * `enabled`: Enables the send a welcome email to a newly registered user. Defaults to `false`.
+  * `sender`: The email address or tuple with `{nickname, email}` that will use as sender to the welcome email.
+  * `subject`: A subject of welcome email.
+  * `html`: A html that will be send to a newly registered users as a email.
+  * `text`: A text that will be send to a newly registered users as a email.
+
+    Example:
+
+  ```elixir
+  config :pleroma, :welcome,
+      direct_message: [
+        enabled: true,
+        sender_nickname: "lain",
+        message: "Hi, @username! Welcome on board!"
+        ],
+      email: [
+        enabled: true,
+        sender: {"Pleroma App", "welcome@pleroma.app"},
+        subject: "Welcome to <%= instance_name %>",
+        html: "Welcome to <%= instance_name %>",
+        text: "Welcome to <%= instance_name %>"
+    ]
+  ```
+
 ## Message rewrite facility
 
 ### :mrf
@@ -934,30 +964,29 @@ Configure OAuth 2 provider capabilities:
 ### :uri_schemes
 * `valid_schemes`: List of the scheme part that is considered valid to be an URL.
 
-### :auto_linker
+### Pleroma.Formatter
 
-Configuration for the `auto_linker` library:
+Configuration for Pleroma's link formatter which parses mentions, hashtags, and URLs.
 
-* `class: "auto-linker"` - specify the class to be added to the generated link. false to clear.
-* `rel: "noopener noreferrer"` - override the rel attribute. false to clear.
-* `new_window: true` - set to false to remove `target='_blank'` attribute.
-* `scheme: false` - Set to true to link urls with schema `http://google.com`.
-* `truncate: false` - Set to a number to truncate urls longer then the number. Truncated urls will end in `..`.
-* `strip_prefix: true` - Strip the scheme prefix.
-* `extra: false` - link urls with rarely used schemes (magnet, ipfs, irc, etc.).
+* `class` - specify the class to be added to the generated link (default: `false`)
+* `rel` - specify the rel attribute (default: `ugc`)
+* `new_window` - adds `target="_blank"` attribute (default: `false`)
+* `truncate` - Set to a number to truncate URLs longer then the number. Truncated URLs will end in `...` (default: `false`)
+* `strip_prefix` - Strip the scheme prefix (default: `false`)
+* `extra` - link URLs with rarely used schemes (magnet, ipfs, irc, etc.) (default: `true`)
+* `validate_tld` - Set to false to disable TLD validation for URLs/emails. Can be set to :no_scheme to validate TLDs only for urls without a scheme (e.g `example.com` will be validated, but `http://example.loki` won't) (default: `:no_scheme`)
 
 Example:
 
 ```elixir
-config :auto_linker,
-  opts: [
-    scheme: true,
-    extra: true,
-    class: false,
-    strip_prefix: false,
-    new_window: false,
-    rel: "ugc"
-  ]
+config :pleroma, Pleroma.Formatter,
+  class: false,
+  rel: "ugc",
+  new_window: false,
+  truncate: false,
+  strip_prefix: false,
+  extra: true,
+  validate_tld: :no_scheme
 ```
 
 ## Custom Runtime Modules (`:modules`)
@@ -1019,3 +1048,25 @@ Note: setting `restrict_unauthenticated/timelines/local` to `true` has no practi
 Control favicons for instances.
 
 * `enabled`: Allow/disallow displaying and getting instances favicons
+
+## Frontend management
+
+Frontends in Pleroma are swappable - you can specify which one to use here.
+
+For now, you can set a frontend with the key `primary` and the options of `name` and `ref`. This will then make Pleroma serve the frontend from a folder constructed by concatenating the instance static path, `frontends` and the name and ref.
+
+The key `primary` refers to the frontend that will be served by default for general requests. In the future, other frontends like the admin frontend will also be configurable here.
+
+If you don't set anything here, the bundled frontend will be used.
+
+Example:
+
+```
+config :pleroma, :frontends,
+  primary: %{
+    "name" => "pleroma",
+    "ref" => "stable"
+  }
+```
+
+This would serve the frontend from the the folder at `$instance_static/frontends/pleroma/stable`. You have to copy the frontend into this folder yourself. You can choose the name and ref any way you like, but they will be used by mix tasks to automate installation in the future, the name referring to the project and the ref referring to a commit.
index 9f0bf6ecbc4fc3e3f858f0708949a7009d1ace28..074492a4698eff861aec3d35cdc51eca50c6e52f 100644 (file)
@@ -24,8 +24,10 @@ defmodule Mix.Pleroma do
       Application.put_env(:logger, :console, level: :debug)
     end
 
+    adapter = Application.get_env(:tesla, :adapter)
+
     apps =
-      if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Gun do
+      if adapter == Tesla.Adapter.Gun do
         [:gun | @apps]
       else
         [:hackney | @apps]
@@ -33,11 +35,14 @@ defmodule Mix.Pleroma do
 
     Enum.each(apps, &Application.ensure_all_started/1)
 
-    children = [
-      Pleroma.Repo,
-      {Pleroma.Config.TransferTask, false},
-      Pleroma.Web.Endpoint
-    ]
+    children =
+      [
+        Pleroma.Repo,
+        {Pleroma.Config.TransferTask, false},
+        Pleroma.Web.Endpoint,
+        {Oban, Pleroma.Config.get(Oban)}
+      ] ++
+        http_children(adapter)
 
     cachex_children = Enum.map(@cachex_children, &Pleroma.Application.build_cachex(&1, []))
 
@@ -115,4 +120,11 @@ defmodule Mix.Pleroma do
   def escape_sh_path(path) do
     ~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(')
   end
+
+  defp http_children(Tesla.Adapter.Gun) do
+    Pleroma.Gun.ConnectionPool.children() ++
+      [{Task, &Pleroma.HTTP.AdapterHelper.Gun.limiter_setup/0}]
+  end
+
+  defp http_children(_), do: []
 end
index 88575a498f6db653c5b40a631961089ad1ba7660..16f62b6f5d43e00caa18e612479f66e82bffcaae 100644 (file)
@@ -16,7 +16,9 @@ defmodule Pleroma.ApplicationRequirements do
   @spec verify!() :: :ok | VerifyError.t()
   def verify! do
     :ok
+    |> check_confirmation_accounts!
     |> check_migrations_applied!()
+    |> check_welcome_message_config!()
     |> check_rum!()
     |> handle_result()
   end
@@ -24,6 +26,40 @@ defmodule Pleroma.ApplicationRequirements do
   defp handle_result(:ok), do: :ok
   defp handle_result({:error, message}), do: raise(VerifyError, message: message)
 
+  defp check_welcome_message_config!(:ok) do
+    if Pleroma.Config.get([:welcome, :email, :enabled], false) and
+         not Pleroma.Emails.Mailer.enabled?() do
+      Logger.error("""
+      To send welcome email do you need to enable mail.
+      \nconfig :pleroma, Pleroma.Emails.Mailer, enabled: true
+      """)
+
+      {:error, "The mail disabled."}
+    else
+      :ok
+    end
+  end
+
+  defp check_welcome_message_config!(result), do: result
+
+  # Checks account confirmation email
+  #
+  def check_confirmation_accounts!(:ok) do
+    if Pleroma.Config.get([:instance, :account_activation_required]) &&
+         not Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled]) do
+      Logger.error(
+        "Account activation enabled, but no Mailer settings enabled.\nPlease set config :pleroma, :instance, account_activation_required: false\nOtherwise setup and enable Mailer."
+      )
+
+      {:error,
+       "Account activation enabled, but Mailer is disabled. Cannot send confirmation emails."}
+    else
+      :ok
+    end
+  end
+
+  def check_confirmation_accounts!(result), do: result
+
   # Checks for pending migrations.
   #
   def check_migrations_applied!(:ok) do
index 1a89d8895bda0c40db8117538ddae08d4a76995d..e5b7811aa51b27530ef020d5bcaa0813018b9798 100644 (file)
@@ -156,7 +156,6 @@ defmodule Pleroma.ConfigDB do
       {:quack, :meta},
       {:mime, :types},
       {:cors_plug, [:max_age, :methods, :expose, :headers]},
-      {:auto_linker, :opts},
       {:swarm, :node_blacklist},
       {:logger, :backends}
     ]
index 026871c4f11b943fdb6cf15e67153138f13be4b8..0f52eb210da75ca3196489e1c5814026de7aa7fa 100644 (file)
@@ -55,6 +55,24 @@ defmodule Pleroma.Config.DeprecationWarnings do
     mrf_user_allowlist()
     check_old_mrf_config()
     check_media_proxy_whitelist_config()
+    check_welcome_message_config()
+  end
+
+  def check_welcome_message_config do
+    instance_config = Pleroma.Config.get([:instance])
+
+    use_old_config =
+      Keyword.has_key?(instance_config, :welcome_user_nickname) or
+        Keyword.has_key?(instance_config, :welcome_message)
+
+    if use_old_config do
+      Logger.error("""
+      !!!DEPRECATION WARNING!!!
+      Your config is using the old namespace for Welcome messages configuration. You need to change to the new namespace:
+      \n* `config :pleroma, :instance, welcome_user_nickname` is now `config :pleroma, :welcome, :direct_message, :sender_nickname`
+      \n* `config :pleroma, :instance, welcome_message` is now `config :pleroma, :welcome, :direct_message, :message`
+      """)
+    end
   end
 
   def check_old_mrf_config do
diff --git a/lib/pleroma/config/helpers.ex b/lib/pleroma/config/helpers.ex
new file mode 100644 (file)
index 0000000..3dce40e
--- /dev/null
@@ -0,0 +1,17 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Config.Helpers do
+  alias Pleroma.Config
+
+  def instance_name, do: Config.get([:instance, :name])
+
+  defp instance_notify_email do
+    Config.get([:instance, :notify_email]) || Config.get([:instance, :email])
+  end
+
+  def sender do
+    {instance_name(), instance_notify_email()}
+  end
+end
index aa0b2a66ba6724b95c8a304b4908e56d55b61966..c27ad10657519915c2b6fd8757576c1475d93709 100644 (file)
@@ -8,6 +8,7 @@ defmodule Pleroma.Emails.AdminEmail do
   import Swoosh.Email
 
   alias Pleroma.Config
+  alias Pleroma.HTML
   alias Pleroma.Web.Router.Helpers
 
   defp instance_config, do: Config.get(:instance)
@@ -82,4 +83,18 @@ defmodule Pleroma.Emails.AdminEmail do
     |> subject("#{instance_name()} Report")
     |> html_body(html_body)
   end
+
+  def new_unapproved_registration(to, account) do
+    html_body = """
+    <p>New account for review: <a href="#{user_url(account)}">@#{account.nickname}</a></p>
+    <blockquote>#{HTML.strip_tags(account.registration_reason)}</blockquote>
+    <a href="#{Pleroma.Web.base_url()}/pleroma/admin">Visit AdminFE</a>
+    """
+
+    new()
+    |> to({to.name, to.email})
+    |> from({instance_name(), instance_notify_email()})
+    |> subject("New account up for review on #{instance_name()} (@#{account.nickname})")
+    |> html_body(html_body)
+  end
 end
index dfadc10b3d962165bb5fba8df5276776646e7a36..3135338593fdc34da8733ad855cefdd574bfddf6 100644 (file)
@@ -12,17 +12,22 @@ defmodule Pleroma.Emails.UserEmail do
   alias Pleroma.Web.Endpoint
   alias Pleroma.Web.Router
 
-  defp instance_name, do: Config.get([:instance, :name])
-
-  defp sender do
-    email = Config.get([:instance, :notify_email]) || Config.get([:instance, :email])
-    {instance_name(), email}
-  end
+  import Pleroma.Config.Helpers, only: [instance_name: 0, sender: 0]
 
   defp recipient(email, nil), do: email
   defp recipient(email, name), do: {name, email}
   defp recipient(%User{} = user), do: recipient(user.email, user.name)
 
+  @spec welcome(User.t(), map()) :: Swoosh.Email.t()
+  def welcome(user, opts \\ %{}) do
+    new()
+    |> to(recipient(user))
+    |> from(Map.get(opts, :sender, sender()))
+    |> subject(Map.get(opts, :subject, "Welcome to #{instance_name()}!"))
+    |> html_body(Map.get(opts, :html, "Welcome to #{instance_name()}!"))
+    |> text_body(Map.get(opts, :text, "Welcome to #{instance_name()}!"))
+  end
+
   def password_reset_email(user, token) when is_binary(token) do
     password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token)
 
index 02a93a8dc2989e68af9594ad549ba7145c02f2a8..0c450eae4ec6e26ac75280cd942556ce2e428440 100644 (file)
@@ -10,11 +10,15 @@ defmodule Pleroma.Formatter do
   @link_regex ~r"((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+"ui
   @markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
 
-  @auto_linker_config hashtag: true,
-                      hashtag_handler: &Pleroma.Formatter.hashtag_handler/4,
-                      mention: true,
-                      mention_handler: &Pleroma.Formatter.mention_handler/4,
-                      scheme: true
+  defp linkify_opts do
+    Pleroma.Config.get(Pleroma.Formatter) ++
+      [
+        hashtag: true,
+        hashtag_handler: &Pleroma.Formatter.hashtag_handler/4,
+        mention: true,
+        mention_handler: &Pleroma.Formatter.mention_handler/4
+      ]
+  end
 
   def escape_mention_handler("@" <> nickname = mention, buffer, _, _) do
     case User.get_cached_by_nickname(nickname) do
@@ -80,19 +84,19 @@ defmodule Pleroma.Formatter do
   @spec linkify(String.t(), keyword()) ::
           {String.t(), [{String.t(), User.t()}], [{String.t(), String.t()}]}
   def linkify(text, options \\ []) do
-    options = options ++ @auto_linker_config
+    options = linkify_opts() ++ options
 
     if options[:safe_mention] && Regex.named_captures(@safe_mention_regex, text) do
       %{"mentions" => mentions, "rest" => rest} = Regex.named_captures(@safe_mention_regex, text)
       acc = %{mentions: MapSet.new(), tags: MapSet.new()}
 
-      {text_mentions, %{mentions: mentions}} = AutoLinker.link_map(mentions, acc, options)
-      {text_rest, %{tags: tags}} = AutoLinker.link_map(rest, acc, options)
+      {text_mentions, %{mentions: mentions}} = Linkify.link_map(mentions, acc, options)
+      {text_rest, %{tags: tags}} = Linkify.link_map(rest, acc, options)
 
       {text_mentions <> text_rest, MapSet.to_list(mentions), MapSet.to_list(tags)}
     else
       acc = %{mentions: MapSet.new(), tags: MapSet.new()}
-      {text, %{mentions: mentions, tags: tags}} = AutoLinker.link_map(text, acc, options)
+      {text, %{mentions: mentions, tags: tags}} = Linkify.link_map(text, acc, options)
 
       {text, MapSet.to_list(mentions), MapSet.to_list(tags)}
     end
@@ -111,9 +115,9 @@ defmodule Pleroma.Formatter do
 
     if options[:safe_mention] && Regex.named_captures(@safe_mention_regex, text) do
       %{"mentions" => mentions, "rest" => rest} = Regex.named_captures(@safe_mention_regex, text)
-      AutoLinker.link(mentions, options) <> AutoLinker.link(rest, options)
+      Linkify.link(mentions, options) <> Linkify.link(rest, options)
     else
-      AutoLinker.link(text, options)
+      Linkify.link(text, options)
     end
   end
 
index 8b41a668c0268c504ec55ddc05bbb7545aedcffe..49e9885bbac3b82d224c901c458a7c41d5225901 100644 (file)
@@ -19,7 +19,7 @@ defmodule Pleroma.Gun.ConnectionPool do
         get_gun_pid_from_worker(worker_pid, true)
 
       [{worker_pid, {gun_pid, _used_by, _crf, _last_reference}}] ->
-        GenServer.cast(worker_pid, {:add_client, self(), false})
+        GenServer.call(worker_pid, :add_client)
         {:ok, gun_pid}
 
       [] ->
@@ -45,7 +45,7 @@ defmodule Pleroma.Gun.ConnectionPool do
     # so instead we use cast + monitor
 
     ref = Process.monitor(worker_pid)
-    if register, do: GenServer.cast(worker_pid, {:add_client, self(), true})
+    if register, do: GenServer.cast(worker_pid, {:add_client, self()})
 
     receive do
       {:conn_pid, pid} ->
@@ -70,7 +70,7 @@ defmodule Pleroma.Gun.ConnectionPool do
 
     case query_result do
       [worker_pid] ->
-        GenServer.cast(worker_pid, {:remove_client, self()})
+        GenServer.call(worker_pid, :remove_client)
 
       [] ->
         :ok
index f33447cb6352c137f88e2b15757c1145bd9445b0..fec9d0efa9daa0323a02e26159c491e00313424d 100644 (file)
@@ -36,7 +36,24 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
   end
 
   @impl true
-  def handle_cast({:add_client, client_pid, send_pid_back}, %{key: key} = state) do
+  def handle_cast({:add_client, client_pid}, state) do
+    case handle_call(:add_client, {client_pid, nil}, state) do
+      {:reply, conn_pid, state, :hibernate} ->
+        send(client_pid, {:conn_pid, conn_pid})
+        {:noreply, state, :hibernate}
+    end
+  end
+
+  @impl true
+  def handle_cast({:remove_client, client_pid}, state) do
+    case handle_call(:remove_client, {client_pid, nil}, state) do
+      {:reply, _, state, :hibernate} ->
+        {:noreply, state, :hibernate}
+    end
+  end
+
+  @impl true
+  def handle_call(:add_client, {client_pid, _}, %{key: key} = state) do
     time = :erlang.monotonic_time(:millisecond)
 
     {{conn_pid, _, _, _}, _} =
@@ -44,8 +61,6 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
         {conn_pid, [client_pid | used_by], crf(time - last_reference, crf), time}
       end)
 
-    if send_pid_back, do: send(client_pid, {:conn_pid, conn_pid})
-
     state =
       if state.timer != nil do
         Process.cancel_timer(state[:timer])
@@ -57,11 +72,11 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
     ref = Process.monitor(client_pid)
 
     state = put_in(state.client_monitors[client_pid], ref)
-    {:noreply, state, :hibernate}
+    {:reply, conn_pid, state, :hibernate}
   end
 
   @impl true
-  def handle_cast({:remove_client, client_pid}, %{key: key} = state) do
+  def handle_call(:remove_client, {client_pid, _}, %{key: key} = state) do
     {{_conn_pid, used_by, _crf, _last_reference}, _} =
       Registry.update_value(@registry, key, fn {conn_pid, used_by, crf, last_reference} ->
         {conn_pid, List.delete(used_by, client_pid), crf, last_reference}
@@ -78,7 +93,7 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
         nil
       end
 
-    {:noreply, %{state | timer: timer}, :hibernate}
+    {:reply, :ok, %{state | timer: timer}, :hibernate}
   end
 
   @impl true
@@ -102,22 +117,13 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
 
   @impl true
   def handle_info({:DOWN, _ref, :process, pid, reason}, state) do
-    # Sometimes the client is dead before we demonitor it in :remove_client, so the message
-    # arrives anyway
+    :telemetry.execute(
+      [:pleroma, :connection_pool, :client_death],
+      %{client_pid: pid, reason: reason},
+      %{key: state.key}
+    )
 
-    case state.client_monitors[pid] do
-      nil ->
-        {:noreply, state, :hibernate}
-
-      _ref ->
-        :telemetry.execute(
-          [:pleroma, :connection_pool, :client_death],
-          %{client_pid: pid, reason: reason},
-          %{key: state.key}
-        )
-
-        handle_cast({:remove_client, pid}, state)
-    end
+    handle_cast({:remove_client, pid}, state)
   end
 
   # LRFU policy: https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.55.1478
index 7aacd9d80984edab08d4f38d408af1d5904e8846..31c9afe2a293a0f46ae944eee0479aa51c5aac57 100644 (file)
@@ -409,6 +409,17 @@ defmodule Pleroma.ModerationLog do
     "@#{actor_nickname} deactivated users: #{users_to_nicknames_string(users)}"
   end
 
+  @spec get_log_entry_message(ModerationLog) :: String.t()
+  def get_log_entry_message(%ModerationLog{
+        data: %{
+          "actor" => %{"nickname" => actor_nickname},
+          "action" => "approve",
+          "subject" => users
+        }
+      }) do
+    "@#{actor_nickname} approved users: #{users_to_nicknames_string(users)}"
+  end
+
   @spec get_log_entry_message(ModerationLog) :: String.t()
   def get_log_entry_message(%ModerationLog{
         data: %{
index 3e2949ee2354f3009ae68142a1a01a5a10b86c6b..e74c87269f2d5b5bc47e039818dae47a3c93b5c8 100644 (file)
@@ -124,6 +124,10 @@ defmodule Pleroma.Object.Fetcher do
       {:error, "Object has been deleted"} ->
         nil
 
+      {:reject, reason} ->
+        Logger.info("Rejected #{id} while fetching: #{inspect(reason)}")
+        nil
+
       e ->
         Logger.error("Error while fetching #{id}: #{inspect(e)}")
         nil
diff --git a/lib/pleroma/plugs/frontend_static.ex b/lib/pleroma/plugs/frontend_static.ex
new file mode 100644 (file)
index 0000000..f549ca7
--- /dev/null
@@ -0,0 +1,54 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Plugs.FrontendStatic do
+  require Pleroma.Constants
+
+  @moduledoc """
+  This is a shim to call `Plug.Static` but with runtime `from` configuration`. It dispatches to the different frontends.
+  """
+  @behaviour Plug
+
+  def file_path(path, frontend_type \\ :primary) do
+    if configuration = Pleroma.Config.get([:frontends, frontend_type]) do
+      instance_static_path = Pleroma.Config.get([:instance, :static_dir], "instance/static")
+
+      Path.join([
+        instance_static_path,
+        "frontends",
+        configuration["name"],
+        configuration["ref"],
+        path
+      ])
+    else
+      nil
+    end
+  end
+
+  def init(opts) do
+    opts
+    |> Keyword.put(:from, "__unconfigured_frontend_static_plug")
+    |> Plug.Static.init()
+  end
+
+  def call(conn, opts) do
+    frontend_type = Map.get(opts, :frontend_type, :primary)
+    path = file_path("", frontend_type)
+
+    if path do
+      conn
+      |> call_static(opts, path)
+    else
+      conn
+    end
+  end
+
+  defp call_static(conn, opts, from) do
+    opts =
+      opts
+      |> Map.put(:from, from)
+
+    Plug.Static.call(conn, opts)
+  end
+end
index 7516f75c38bd747f37823a44b8bba4544aed102c..0fb57e42257f5927cd6c7e6299782d8526ff9c4f 100644 (file)
@@ -16,28 +16,24 @@ defmodule Pleroma.Plugs.InstanceStatic do
     instance_path =
       Path.join(Pleroma.Config.get([:instance, :static_dir], "instance/static/"), path)
 
-    if File.exists?(instance_path) do
-      instance_path
-    else
+    frontend_path = Pleroma.Plugs.FrontendStatic.file_path(path, :primary)
+
+    (File.exists?(instance_path) && instance_path) ||
+      (frontend_path && File.exists?(frontend_path) && frontend_path) ||
       Path.join(Application.app_dir(:pleroma, "priv/static/"), path)
-    end
   end
 
   def init(opts) do
     opts
     |> Keyword.put(:from, "__unconfigured_instance_static_plug")
-    |> Keyword.put(:at, "/__unconfigured_instance_static_plug")
     |> Plug.Static.init()
   end
 
   for only <- Pleroma.Constants.static_only_files() do
-    at = Plug.Router.Utils.split("/")
-
     def call(%{request_path: "/" <> unquote(only) <> _} = conn, opts) do
       call_static(
         conn,
         opts,
-        unquote(at),
         Pleroma.Config.get([:instance, :static_dir], "instance/static")
       )
     end
@@ -47,11 +43,10 @@ defmodule Pleroma.Plugs.InstanceStatic do
     conn
   end
 
-  defp call_static(conn, opts, at, from) do
+  defp call_static(conn, opts, from) do
     opts =
       opts
       |> Map.put(:from, from)
-      |> Map.put(:at, at)
 
     Plug.Static.call(conn, opts)
   end
index 65785445d2a5021d7ee4d57062b64315c653bc18..d5a339681a752d6ae301ed08c33a49ca4b36353f 100644 (file)
@@ -5,6 +5,8 @@
 defmodule Pleroma.ReverseProxy.Client.Tesla do
   @behaviour Pleroma.ReverseProxy.Client
 
+  alias Pleroma.Gun.ConnectionPool
+
   @type headers() :: [{String.t(), String.t()}]
   @type status() :: pos_integer()
 
@@ -31,6 +33,8 @@ defmodule Pleroma.ReverseProxy.Client.Tesla do
       if is_map(response.body) and method != :head do
         {:ok, response.status, response.headers, response.body}
       else
+        conn_pid = response.opts[:adapter][:conn]
+        ConnectionPool.release_conn(conn_pid)
         {:ok, response.status, response.headers}
       end
     else
@@ -41,15 +45,8 @@ defmodule Pleroma.ReverseProxy.Client.Tesla do
   @impl true
   @spec stream_body(map()) ::
           {:ok, binary(), map()} | {:error, atom() | String.t()} | :done | no_return()
-  def stream_body(%{pid: pid, opts: opts, fin: true}) do
-    # if connection was reused, but in tesla were redirects,
-    # tesla returns new opened connection, which must be closed manually
-    if opts[:old_conn], do: Tesla.Adapter.Gun.close(pid)
-    # if there were redirects we need to checkout old conn
-    conn = opts[:old_conn] || opts[:conn]
-
-    if conn, do: :ok = Pleroma.Gun.ConnectionPool.release_conn(conn)
-
+  def stream_body(%{pid: pid, fin: true}) do
+    ConnectionPool.release_conn(pid)
     :done
   end
 
@@ -74,8 +71,7 @@ defmodule Pleroma.ReverseProxy.Client.Tesla do
   @impl true
   @spec close(map) :: :ok | no_return()
   def close(%{pid: pid}) do
-    adapter = check_adapter()
-    adapter.close(pid)
+    ConnectionPool.release_conn(pid)
   end
 
   defp check_adapter do
index 28ad4c8460a92a7690b55ac3c92db6b66df6bff3..0de4e2309c5e30f8816d3f68fd50071696184900 100644 (file)
@@ -165,6 +165,9 @@ defmodule Pleroma.ReverseProxy do
       {:ok, code, _, _} ->
         {:error, {:invalid_http_response, code}}
 
+      {:ok, code, _} ->
+        {:error, {:invalid_http_response, code}}
+
       {:error, error} ->
         {:error, error}
     end
index 9240e912d9a3db676b6f19a2a95e2b932a4bf661..dcf6ebee2e4a46d8c6f4150e03a8fd4c7d561fac 100644 (file)
@@ -42,7 +42,12 @@ defmodule Pleroma.User do
   require Logger
 
   @type t :: %__MODULE__{}
-  @type account_status :: :active | :deactivated | :password_reset_pending | :confirmation_pending
+  @type account_status ::
+          :active
+          | :deactivated
+          | :password_reset_pending
+          | :confirmation_pending
+          | :approval_pending
   @primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true}
 
   # credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
@@ -106,6 +111,8 @@ defmodule Pleroma.User do
     field(:locked, :boolean, default: false)
     field(:confirmation_pending, :boolean, default: false)
     field(:password_reset_pending, :boolean, default: false)
+    field(:approval_pending, :boolean, default: false)
+    field(:registration_reason, :string, default: nil)
     field(:confirmation_token, :string, default: nil)
     field(:default_scope, :string, default: "public")
     field(:domain_blocks, {:array, :string}, default: [])
@@ -262,6 +269,7 @@ defmodule Pleroma.User do
   @spec account_status(User.t()) :: account_status()
   def account_status(%User{deactivated: true}), do: :deactivated
   def account_status(%User{password_reset_pending: true}), do: :password_reset_pending
+  def account_status(%User{approval_pending: true}), do: :approval_pending
 
   def account_status(%User{confirmation_pending: true}) do
     if Config.get([:instance, :account_activation_required]) do
@@ -633,6 +641,7 @@ defmodule Pleroma.User do
   def register_changeset(struct, params \\ %{}, opts \\ []) do
     bio_limit = Config.get([:instance, :user_bio_length], 5000)
     name_limit = Config.get([:instance, :user_name_length], 100)
+    reason_limit = Config.get([:instance, :registration_reason_length], 500)
     params = Map.put_new(params, :accepts_chat_messages, true)
 
     need_confirmation? =
@@ -642,8 +651,16 @@ defmodule Pleroma.User do
         opts[:need_confirmation]
       end
 
+    need_approval? =
+      if is_nil(opts[:need_approval]) do
+        Config.get([:instance, :account_approval_required])
+      else
+        opts[:need_approval]
+      end
+
     struct
     |> confirmation_changeset(need_confirmation: need_confirmation?)
+    |> approval_changeset(need_approval: need_approval?)
     |> cast(params, [
       :bio,
       :raw_bio,
@@ -653,7 +670,8 @@ defmodule Pleroma.User do
       :password,
       :password_confirmation,
       :emoji,
-      :accepts_chat_messages
+      :accepts_chat_messages,
+      :registration_reason
     ])
     |> validate_required([:name, :nickname, :password, :password_confirmation])
     |> validate_confirmation(:password)
@@ -664,6 +682,7 @@ defmodule Pleroma.User do
     |> validate_format(:email, @email_regex)
     |> validate_length(:bio, max: bio_limit)
     |> validate_length(:name, min: 1, max: name_limit)
+    |> validate_length(:registration_reason, max: reason_limit)
     |> maybe_validate_required_email(opts[:external])
     |> put_password_hash
     |> put_ap_id()
@@ -713,27 +732,52 @@ defmodule Pleroma.User do
   def post_register_action(%User{} = user) do
     with {:ok, user} <- autofollow_users(user),
          {:ok, user} <- set_cache(user),
-         {:ok, _} <- User.WelcomeMessage.post_welcome_message_to_user(user),
+         {:ok, _} <- send_welcome_email(user),
+         {:ok, _} <- send_welcome_message(user),
          {:ok, _} <- try_send_confirmation_email(user) do
       {:ok, user}
     end
   end
 
-  def try_send_confirmation_email(%User{} = user) do
-    if user.confirmation_pending &&
-         Config.get([:instance, :account_activation_required]) do
-      user
-      |> Pleroma.Emails.UserEmail.account_confirmation_email()
-      |> Pleroma.Emails.Mailer.deliver_async()
+  def send_welcome_message(user) do
+    if User.WelcomeMessage.enabled?() do
+      User.WelcomeMessage.post_message(user)
+      {:ok, :enqueued}
+    else
+      {:ok, :noop}
+    end
+  end
+
+  def send_welcome_email(%User{email: email} = user) when is_binary(email) do
+    if User.WelcomeEmail.enabled?() do
+      User.WelcomeEmail.send_email(user)
+      {:ok, :enqueued}
+    else
+      {:ok, :noop}
+    end
+  end
+
+  def send_welcome_email(_), do: {:ok, :noop}
 
+  @spec try_send_confirmation_email(User.t()) :: {:ok, :enqueued | :noop}
+  def try_send_confirmation_email(%User{confirmation_pending: true} = user) do
+    if Config.get([:instance, :account_activation_required]) do
+      send_confirmation_email(user)
       {:ok, :enqueued}
     else
       {:ok, :noop}
     end
   end
 
-  def try_send_confirmation_email(users) do
-    Enum.each(users, &try_send_confirmation_email/1)
+  def try_send_confirmation_email(_), do: {:ok, :noop}
+
+  @spec send_confirmation_email(Uset.t()) :: User.t()
+  def send_confirmation_email(%User{} = user) do
+    user
+    |> Pleroma.Emails.UserEmail.account_confirmation_email()
+    |> Pleroma.Emails.Mailer.deliver_async()
+
+    user
   end
 
   def needs_update?(%User{local: true}), do: false
@@ -1469,6 +1513,19 @@ defmodule Pleroma.User do
     end
   end
 
+  def approve(users) when is_list(users) do
+    Repo.transaction(fn ->
+      Enum.map(users, fn user ->
+        with {:ok, user} <- approve(user), do: user
+      end)
+    end)
+  end
+
+  def approve(%User{} = user) do
+    change(user, approval_pending: false)
+    |> update_and_set_cache()
+  end
+
   def update_notification_settings(%User{} = user, settings) do
     user
     |> cast(%{notification_settings: settings}, [])
@@ -1495,12 +1552,17 @@ defmodule Pleroma.User do
   defp delete_or_deactivate(%User{local: true} = user) do
     status = account_status(user)
 
-    if status == :confirmation_pending do
-      delete_and_invalidate_cache(user)
-    else
-      user
-      |> change(%{deactivated: true, email: nil})
-      |> update_and_set_cache()
+    case status do
+      :confirmation_pending ->
+        delete_and_invalidate_cache(user)
+
+      :approval_pending ->
+        delete_and_invalidate_cache(user)
+
+      _ ->
+        user
+        |> change(%{deactivated: true, email: nil})
+        |> update_and_set_cache()
     end
   end
 
@@ -2153,6 +2215,12 @@ defmodule Pleroma.User do
     cast(user, params, [:confirmation_pending, :confirmation_token])
   end
 
+  @spec approval_changeset(User.t(), keyword()) :: Changeset.t()
+  def approval_changeset(user, need_approval: need_approval?) do
+    params = if need_approval?, do: %{approval_pending: true}, else: %{approval_pending: false}
+    cast(user, params, [:approval_pending])
+  end
+
   def add_pinnned_activity(user, %Pleroma.Activity{id: id}) do
     if id not in user.pinned_activities do
       max_pinned_statuses = Config.get([:instance, :max_pinned_statuses], 0)
index 66ffe909031637f284f66c2450a8701ffc4e8add..45553cb6c9a6b407f4aace4ce7c917f508cf6aec 100644 (file)
@@ -42,6 +42,7 @@ defmodule Pleroma.User.Query do
             external: boolean(),
             active: boolean(),
             deactivated: boolean(),
+            need_approval: boolean(),
             is_admin: boolean(),
             is_moderator: boolean(),
             super_users: boolean(),
@@ -146,6 +147,10 @@ defmodule Pleroma.User.Query do
     |> where([u], not is_nil(u.nickname))
   end
 
+  defp compose_query({:need_approval, _}, query) do
+    where(query, [u], u.approval_pending)
+  end
+
   defp compose_query({:followers, %User{id: id}}, query) do
     query
     |> where([u], u.id != ^id)
diff --git a/lib/pleroma/user/welcome_email.ex b/lib/pleroma/user/welcome_email.ex
new file mode 100644 (file)
index 0000000..5322000
--- /dev/null
@@ -0,0 +1,62 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.User.WelcomeEmail do
+  @moduledoc """
+  The module represents the functions to send welcome email.
+  """
+
+  alias Pleroma.Config
+  alias Pleroma.Emails
+  alias Pleroma.User
+
+  import Pleroma.Config.Helpers, only: [instance_name: 0]
+
+  @spec enabled?() :: boolean()
+  def enabled?, do: Config.get([:welcome, :email, :enabled], false)
+
+  @spec send_email(User.t()) :: {:ok, Oban.Job.t()}
+  def send_email(%User{} = user) do
+    user
+    |> Emails.UserEmail.welcome(email_options(user))
+    |> Emails.Mailer.deliver_async()
+  end
+
+  defp email_options(user) do
+    bindings = [user: user, instance_name: instance_name()]
+
+    %{}
+    |> add_sender(Config.get([:welcome, :email, :sender], nil))
+    |> add_option(:subject, bindings)
+    |> add_option(:html, bindings)
+    |> add_option(:text, bindings)
+  end
+
+  defp add_option(opts, option, bindings) do
+    [:welcome, :email, option]
+    |> Config.get(nil)
+    |> eval_string(bindings)
+    |> merge_options(opts, option)
+  end
+
+  defp add_sender(opts, {_name, _email} = sender) do
+    merge_options(sender, opts, :sender)
+  end
+
+  defp add_sender(opts, sender) when is_binary(sender) do
+    add_sender(opts, {instance_name(), sender})
+  end
+
+  defp add_sender(opts, _), do: opts
+
+  defp merge_options(nil, options, _option), do: options
+
+  defp merge_options(value, options, option) do
+    Map.merge(options, %{option => value})
+  end
+
+  defp eval_string(nil, _), do: nil
+  defp eval_string("", _), do: nil
+  defp eval_string(str, bindings), do: EEx.eval_string(str, bindings)
+end
index f8f52028595b7128a864bc611b90454920782bb1..86e1c0678b71e842f67a857fd5f1a4b7593f2fa7 100644 (file)
@@ -3,32 +3,45 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.User.WelcomeMessage do
+  alias Pleroma.Config
   alias Pleroma.User
   alias Pleroma.Web.CommonAPI
 
-  def post_welcome_message_to_user(user) do
-    with %User{} = sender_user <- welcome_user(),
-         message when is_binary(message) <- welcome_message() do
-      CommonAPI.post(sender_user, %{
+  @spec enabled?() :: boolean()
+  def enabled?, do: Config.get([:welcome, :direct_message, :enabled], false)
+
+  @spec post_message(User.t()) :: {:ok, Pleroma.Activity.t() | nil}
+  def post_message(user) do
+    [:welcome, :direct_message, :sender_nickname]
+    |> Config.get(nil)
+    |> fetch_sender()
+    |> do_post(user, welcome_message())
+  end
+
+  defp do_post(%User{} = sender, %User{nickname: nickname}, message)
+       when is_binary(message) do
+    CommonAPI.post(
+      sender,
+      %{
         visibility: "direct",
-        status: "@#{user.nickname}\n#{message}"
-      })
-    else
-      _ -> {:ok, nil}
-    end
+        status: "@#{nickname}\n#{message}"
+      }
+    )
   end
 
-  defp welcome_user do
-    with nickname when is_binary(nickname) <-
-           Pleroma.Config.get([:instance, :welcome_user_nickname]),
-         %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
+  defp do_post(_sender, _recipient, _message), do: {:ok, nil}
+
+  defp fetch_sender(nickname) when is_binary(nickname) do
+    with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
       user
     else
       _ -> nil
     end
   end
 
+  defp fetch_sender(_), do: nil
+
   defp welcome_message do
-    Pleroma.Config.get([:instance, :welcome_message])
+    Config.get([:welcome, :direct_message, :message], nil)
   end
 end
index bc7b5d95a6925e98d5f67237d832f6be50322785..a4db1d87c42334432396e10ccc2cf6d41176a363 100644 (file)
@@ -1370,6 +1370,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
         Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
         {:error, e}
 
+      {:error, {:reject, reason} = e} ->
+        Logger.info("Rejected user #{ap_id}: #{inspect(reason)}")
+        {:error, e}
+
       {:error, e} ->
         Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
         {:error, e}
index 2627a00073428d38dd2df75f07ec3d679351a740..3bf70b8946b90f9248607d15ceaecbb3e63cc7e2 100644 (file)
@@ -27,7 +27,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
 
   def filter_by_summary(_in_reply_to, child), do: child
 
-  def filter(%{"type" => "Create", "object" => child_object} = object) do
+  def filter(%{"type" => "Create", "object" => child_object} = object)
+      when is_map(child_object) do
     child =
       child_object["inReplyTo"]
       |> Object.normalize(child_object["inReplyTo"])
index df926829c4eb42d36201df45f2c2dc13e7bfe6b5..0dcc7be4dbd778a1eebace51c8848e4f86cb76cd 100644 (file)
@@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
   the system.
   """
 
+  alias Pleroma.Activity
   alias Pleroma.EctoType.ActivityPub.ObjectValidators
   alias Pleroma.Object
   alias Pleroma.User
@@ -71,6 +72,12 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
            |> UndoValidator.cast_and_validate()
            |> Ecto.Changeset.apply_action(:insert) do
       object = stringify_keys(object)
+      undone_object = Activity.get_by_ap_id(object["object"])
+
+      meta =
+        meta
+        |> Keyword.put(:object_data, undone_object.data)
+
       {:ok, object, meta}
     end
   end
index 6875c47f67e230e412b1f1f050bbc1e170326256..36e325c373d1463c9ac21f7a51913bf440201617 100644 (file)
@@ -52,6 +52,13 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
       do_not_federate = meta[:do_not_federate] || !Config.get([:instance, :federating])
 
       if !do_not_federate && local do
+        activity =
+          if object = Keyword.get(meta, :object_data) do
+            %{activity | data: Map.put(activity.data, "object", object)}
+          else
+            activity
+          end
+
         Federator.publish(activity)
         {:ok, :federated}
       else
index f37bcab3e752493f090c671ef2955ed3ae2159c3..35aa05eb5a1025bfd725dd5d11f7acd739d19c5f 100644 (file)
@@ -178,7 +178,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
         |> Map.drop(["conversation"])
       else
         e ->
-          Logger.error("Couldn't fetch #{inspect(in_reply_to_id)}, error: #{inspect(e)}")
+          Logger.warn("Couldn't fetch #{inspect(in_reply_to_id)}, error: #{inspect(e)}")
           object
       end
     else
index dfae602dfea61a6c01496f54c51fe289cb40ce05..713b0ca1f3ea1a3bcf14accd02dcf928f7373fa9 100644 (file)
@@ -719,15 +719,18 @@ defmodule Pleroma.Web.ActivityPub.Utils do
 
     case Activity.get_by_ap_id_with_object(id) do
       %Activity{} = activity ->
+        activity_actor = User.get_by_ap_id(activity.object.data["actor"])
+
         %{
           "type" => "Note",
           "id" => activity.data["id"],
           "content" => activity.object.data["content"],
           "published" => activity.object.data["published"],
           "actor" =>
-            AccountView.render("show.json", %{
-              user: User.get_by_ap_id(activity.object.data["actor"])
-            })
+            AccountView.render(
+              "show.json",
+              %{user: activity_actor, skip_visibility_check: true}
+            )
         }
 
       _ ->
index e5f14269a1992fc9218b3a9f1ca87aa8638b18f5..aa2af1ab5495a4715f79d11c0a8a2bffc1ae01e5 100644 (file)
@@ -44,6 +44,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
            :user_toggle_activation,
            :user_activate,
            :user_deactivate,
+           :user_approve,
            :tag_users,
            :untag_users,
            :right_add,
@@ -303,6 +304,21 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     |> render("index.json", %{users: Keyword.values(updated_users)})
   end
 
+  def user_approve(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
+    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
+    {:ok, updated_users} = User.approve(users)
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: users,
+      action: "approve"
+    })
+
+    conn
+    |> put_view(AccountView)
+    |> render("index.json", %{users: updated_users})
+  end
+
   def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
     with {:ok, _} <- User.tag(nicknames, tags) do
       ModerationLog.insert_log(%{
@@ -345,12 +361,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)) do
       json(
         conn,
-        AccountView.render("index.json", users: users, count: count, page_size: page_size)
+        AccountView.render("index.json",
+          users: users,
+          count: count,
+          page_size: page_size
+        )
       )
     end
   end
 
-  @filters ~w(local external active deactivated is_admin is_moderator)
+  @filters ~w(local external active deactivated need_approval is_admin is_moderator)
 
   @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
   defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
@@ -616,29 +636,24 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   end
 
   def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
-    users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
+    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
 
     User.toggle_confirmation(users)
 
-    ModerationLog.insert_log(%{
-      actor: admin,
-      subject: users,
-      action: "confirm_email"
-    })
+    ModerationLog.insert_log(%{actor: admin, subject: users, action: "confirm_email"})
 
     json(conn, "")
   end
 
   def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
-    users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
-
-    User.try_send_confirmation_email(users)
+    users =
+      Enum.map(nicknames, fn nickname ->
+        nickname
+        |> User.get_cached_by_nickname()
+        |> User.send_confirmation_email()
+      end)
 
-    ModerationLog.insert_log(%{
-      actor: admin,
-      subject: users,
-      action: "resend_confirmation_email"
-    })
+    ModerationLog.insert_log(%{actor: admin, subject: users, action: "resend_confirmation_email"})
 
     json(conn, "")
   end
index e1e92963251f87f79c4847e3129e8e5eaa715fde..333e72e42ab083743378d14b969dfa5fee810183 100644 (file)
@@ -77,7 +77,9 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
       "roles" => User.roles(user),
       "tags" => user.tags || [],
       "confirmation_pending" => user.confirmation_pending,
-      "url" => user.uri || user.ap_id
+      "approval_pending" => user.approval_pending,
+      "url" => user.uri || user.ap_id,
+      "registration_reason" => user.registration_reason
     }
   end
 
@@ -105,7 +107,7 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
   end
 
   def merge_account_views(%User{} = user) do
-    MastodonAPI.AccountView.render("show.json", %{user: user})
+    MastodonAPI.AccountView.render("show.json", %{user: user, skip_visibility_check: true})
     |> Map.merge(AdminAPI.AccountView.render("show.json", %{user: user}))
   end
 
index cf299bfc264a39cbe05ed9fc554095bf1b7a45ad..b1a0d26ab6e51f3315db9e7508c19717c6b91eef 100644 (file)
@@ -300,11 +300,11 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do
           "content" => "Check this out :firefox:",
           "id" => "13",
           "chat_id" => "1",
-          "actor_id" => "someflakeid",
+          "account_id" => "someflakeid",
           "unread" => false
         },
         %{
-          "actor_id" => "someflakeid",
+          "account_id" => "someflakeid",
           "content" => "Whats' up?",
           "id" => "12",
           "chat_id" => "1",
index 049bcf93139c5b1005bc292159c021922269fdd5..1e0da820966146a0a4954ba7e3c1d492dbf4bdf9 100644 (file)
@@ -31,6 +31,7 @@ defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do
     }
   end
 
+  # Supporting domain query parameter is deprecated in Mastodon API
   def create_operation do
     %Operation{
       tags: ["domain_blocks"],
@@ -45,11 +46,13 @@ defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do
       """,
       operationId: "DomainBlockController.create",
       requestBody: domain_block_request(),
+      parameters: [Operation.parameter(:domain, :query, %Schema{type: :string}, "Domain name")],
       security: [%{"oAuth" => ["follow", "write:blocks"]}],
       responses: %{200 => empty_object_response()}
     }
   end
 
+  # Supporting domain query parameter is deprecated in Mastodon API
   def delete_operation do
     %Operation{
       tags: ["domain_blocks"],
@@ -57,6 +60,7 @@ defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do
       description: "Remove a domain block, if it exists in the user's array of blocked domains.",
       operationId: "DomainBlockController.delete",
       requestBody: domain_block_request(),
+      parameters: [Operation.parameter(:domain, :query, %Schema{type: :string}, "Domain name")],
       security: [%{"oAuth" => ["follow", "write:blocks"]}],
       responses: %{
         200 => Operation.response("Empty object", "application/json", %Schema{type: :object})
@@ -71,10 +75,9 @@ defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do
         type: :object,
         properties: %{
           domain: %Schema{type: :string}
-        },
-        required: [:domain]
+        }
       },
-      required: true,
+      required: false,
       example: %{
         "domain" => "facebook.com"
       }
index bce27897fd9b17a6559e704309da16f0dde3ded3..3b1469c19a042d31b030d37d6761ed312f7b3558 100644 (file)
@@ -4,8 +4,10 @@
 
 defmodule Pleroma.Web.ChatChannel do
   use Phoenix.Channel
+
   alias Pleroma.User
   alias Pleroma.Web.ChatChannel.ChatChannelState
+  alias Pleroma.Web.MastodonAPI.AccountView
 
   def join("chat:public", _message, socket) do
     send(self(), :after_join)
@@ -22,9 +24,9 @@ defmodule Pleroma.Web.ChatChannel do
 
     if String.length(text) in 1..Pleroma.Config.get([:instance, :chat_limit]) do
       author = User.get_cached_by_nickname(user_name)
-      author = Pleroma.Web.MastodonAPI.AccountView.render("show.json", user: author)
+      author_json = AccountView.render("show.json", user: author, skip_visibility_check: true)
 
-      message = ChatChannelState.add_message(%{text: text, author: author})
+      message = ChatChannelState.add_message(%{text: text, author: author_json})
 
       broadcast!(socket, "new_msg", message)
     end
index 226d42c2c7a0171922f668c56bd4f5816b131728..527fb288d39766e101ead334d0571904e10dce46 100644 (file)
@@ -28,6 +28,17 @@ defmodule Pleroma.Web.Endpoint do
     }
   )
 
+  # Careful! No `only` restriction here, as we don't know what frontends contain.
+  plug(Pleroma.Plugs.FrontendStatic,
+    at: "/",
+    frontend_type: :primary,
+    gzip: true,
+    cache_control_for_etags: @static_cache_control,
+    headers: %{
+      "cache-control" => @static_cache_control
+    }
+  )
+
   # Serve at "/" the static files from "priv/static" directory.
   #
   # You should set gzip to true if you are running phoenix.digest
index d56f438184fa920457ad4f8d6ceaf5f5b6b932a4..9cd334a3350257f325a493a3b414460c1faeeb34 100644 (file)
@@ -47,7 +47,7 @@ defmodule Pleroma.Web.Feed.UserController do
         "atom"
       end
 
-    with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
+    with {_, %User{local: true} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
       activities =
         %{
           type: ["Create"],
@@ -71,6 +71,7 @@ defmodule Pleroma.Web.Feed.UserController do
     render_error(conn, :not_found, "Not found")
   end
 
+  def errors(conn, {:fetch_user, %User{local: false}}), do: errors(conn, {:error, :not_found})
   def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found})
 
   def errors(conn, _) do
index 825b231ab3b02526041c11af5eece7cbe09cebd5..9c2d093cdb493946d66689b53b362694660e8de2 100644 (file)
@@ -32,9 +32,19 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do
     json(conn, %{})
   end
 
+  def create(%{assigns: %{user: blocker}} = conn, %{domain: domain}) do
+    User.block_domain(blocker, domain)
+    json(conn, %{})
+  end
+
   @doc "DELETE /api/v1/domain_blocks"
   def delete(%{assigns: %{user: blocker}, body_params: %{domain: domain}} = conn, _params) do
     User.unblock_domain(blocker, domain)
     json(conn, %{})
   end
+
+  def delete(%{assigns: %{user: blocker}} = conn, %{domain: domain}) do
+    User.unblock_domain(blocker, domain)
+    json(conn, %{})
+  end
 end
index 29affa7d54e5d8162b79a1f662fe3c8781c07dfe..5a983db3994509ccc133decf6afe53a09a1f161f 100644 (file)
@@ -93,7 +93,6 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
     AccountView.render("index.json",
       users: accounts,
       for: options[:for_user],
-      as: :user,
       embed_relationships: options[:embed_relationships]
     )
   end
index bc9745044a4bef61f2b0ae00786797b37f987858..864c0417f14a2694ac9cb0583d118ceb4d4806c4 100644 (file)
@@ -27,21 +27,40 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
           UserRelationship.view_relationships_option(reading_user, users)
       end
 
-    opts = Map.put(opts, :relationships, relationships_opt)
+    opts =
+      opts
+      |> Map.merge(%{relationships: relationships_opt, as: :user})
+      |> Map.delete(:users)
 
     users
     |> render_many(AccountView, "show.json", opts)
     |> Enum.filter(&Enum.any?/1)
   end
 
-  def render("show.json", %{user: user} = opts) do
-    if User.visible_for(user, opts[:for]) == :visible do
+  @doc """
+  Renders specified user account.
+    :skip_visibility_check option skips visibility check and renders any user (local or remote)
+      regardless of [:pleroma, :restrict_unauthenticated] setting.
+    :for option specifies the requester and can be a User record or nil.
+      Only use `user: user, for: user` when `user` is the actual requester of own profile.
+  """
+  def render("show.json", %{user: _user, skip_visibility_check: true} = opts) do
+    do_render("show.json", opts)
+  end
+
+  def render("show.json", %{user: user, for: for_user_or_nil} = opts) do
+    if User.visible_for(user, for_user_or_nil) == :visible do
       do_render("show.json", opts)
     else
       %{}
     end
   end
 
+  def render("show.json", _) do
+    raise "In order to prevent account accessibility issues, " <>
+            ":skip_visibility_check or :for option is required."
+  end
+
   def render("mention.json", %{user: user}) do
     %{
       id: to_string(user.id),
index 06f0c172865406bd10f2a8c70a8583e704dd1fd5..a91994915facbf3c01afa39383f35e0881ecda1e 100644 (file)
@@ -38,7 +38,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do
 
     %{
       id: participation.id |> to_string(),
-      accounts: render(AccountView, "index.json", users: users, as: :user),
+      accounts: render(AccountView, "index.json", users: users, for: user),
       unread: !participation.read,
       last_status:
         render(StatusView, "show.json",
index cd3bc7f00c525f5413a6be3e38613dc44e3b9167..ea2d3aa9c9603cb26ee30b4bb7c9bb6c42fd8a3c 100644 (file)
@@ -26,6 +26,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
       thumbnail: Keyword.get(instance, :instance_thumbnail),
       languages: ["en"],
       registrations: Keyword.get(instance, :registrations_open),
+      approval_required: Keyword.get(instance, :account_approval_required),
       # Extra (not present in Mastodon):
       max_toot_chars: Keyword.get(instance, :limit),
       poll_limits: Keyword.get(instance, :poll_limits),
index fa9d695f306912fb176aa62087604b034a3d2804..91b41ef59cda66ddaa4cf5aa7c5d11cfdc4db083 100644 (file)
@@ -297,13 +297,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
 
     emoji_reactions =
       with %{data: %{"reactions" => emoji_reactions}} <- object do
-        Enum.map(emoji_reactions, fn [emoji, users] ->
-          %{
-            name: emoji,
-            count: length(users),
-            me: !!(opts[:for] && opts[:for].ap_id in users)
-          }
+        Enum.map(emoji_reactions, fn
+          [emoji, users] when is_list(users) ->
+            build_emoji_map(emoji, users, opts[:for])
+
+          {emoji, users} when is_list(users) ->
+            build_emoji_map(emoji, users, opts[:for])
+
+          _ ->
+            nil
         end)
+        |> Enum.reject(&is_nil/1)
       else
         _ -> []
       end
@@ -545,4 +549,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
 
   defp pinned?(%Activity{id: id}, %User{pinned_activities: pinned_activities}),
     do: id in pinned_activities
+
+  defp build_emoji_map(emoji, users, current_user) do
+    %{
+      name: emoji,
+      count: length(users),
+      me: !!(current_user && current_user.ap_id in users)
+    }
+  end
 end
index 7683589cf2b347fb6d604c1e7a5bca788710041f..61fe81d331fc0bc0e40e0a099aa86ee533df242d 100644 (file)
@@ -337,6 +337,16 @@ defmodule Pleroma.Web.OAuth.OAuthController do
     )
   end
 
+  defp handle_token_exchange_error(%Plug.Conn{} = conn, {:account_status, :approval_pending}) do
+    render_error(
+      conn,
+      :forbidden,
+      "Your account is awaiting approval.",
+      %{},
+      "awaiting_approval"
+    )
+  end
+
   defp handle_token_exchange_error(%Plug.Conn{} = conn, _error) do
     render_invalid_credentials_error(conn)
   end
index c8ef3d91511a247531749918bb981ec79e592687..e8a1746d46a82d459dedc10799c58e92f4af2063 100644 (file)
@@ -89,11 +89,11 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
          cm_ref <- MessageReference.for_chat_and_object(chat, message) do
       conn
       |> put_view(MessageReferenceView)
-      |> render("show.json", for: user, chat_message_reference: cm_ref)
+      |> render("show.json", chat_message_reference: cm_ref)
     end
   end
 
-  def mark_message_as_read(%{assigns: %{user: %{id: user_id} = user}} = conn, %{
+  def mark_message_as_read(%{assigns: %{user: %{id: user_id}}} = conn, %{
         id: chat_id,
         message_id: message_id
       }) do
@@ -104,12 +104,15 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
          {:ok, cm_ref} <- MessageReference.mark_as_read(cm_ref) do
       conn
       |> put_view(MessageReferenceView)
-      |> render("show.json", for: user, chat_message_reference: cm_ref)
+      |> render("show.json", chat_message_reference: cm_ref)
     end
   end
 
   def mark_as_read(
-        %{body_params: %{last_read_id: last_read_id}, assigns: %{user: %{id: user_id}}} = conn,
+        %{
+          body_params: %{last_read_id: last_read_id},
+          assigns: %{user: %{id: user_id}}
+        } = conn,
         %{id: id}
       ) do
     with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id),
@@ -121,7 +124,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
     end
   end
 
-  def messages(%{assigns: %{user: %{id: user_id} = user}} = conn, %{id: id} = params) do
+  def messages(%{assigns: %{user: %{id: user_id}}} = conn, %{id: id} = params) do
     with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id) do
       cm_refs =
         chat
@@ -130,7 +133,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
 
       conn
       |> put_view(MessageReferenceView)
-      |> render("index.json", for: user, chat_message_references: cm_refs)
+      |> render("index.json", chat_message_references: cm_refs)
     else
       _ ->
         conn
index 33ecd1f70979ce27af7e335f58e8f8c0fe5ff647..657f4632451a192a0bf34dccb1bfe928d5e3c5d6 100644 (file)
@@ -21,8 +21,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
          ]
   )
 
-  @skip_plugs [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug]
-  plug(:skip_plug, @skip_plugs when action in [:archive, :show, :list])
+  @skip_plugs [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug]
+  plug(:skip_plug, @skip_plugs when action in [:index, :show, :archive])
 
   defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaEmojiPackOperation
 
index 1c996da119474501a145a75e766f8cf7044a0ecf..04dc20d511b918fae999fb57aa2f6e953976204d 100644 (file)
@@ -15,10 +15,11 @@ defmodule Pleroma.Web.PleromaAPI.ChatView do
   def render("show.json", %{chat: %Chat{} = chat} = opts) do
     recipient = User.get_cached_by_ap_id(chat.recipient)
     last_message = opts[:last_message] || MessageReference.last_message_for_chat(chat)
+    account_view_opts = account_view_opts(opts, recipient)
 
     %{
       id: chat.id |> to_string(),
-      account: AccountView.render("show.json", Map.put(opts, :user, recipient)),
+      account: AccountView.render("show.json", account_view_opts),
       unread: MessageReference.unread_count_for_chat(chat),
       last_message:
         last_message &&
@@ -27,7 +28,17 @@ defmodule Pleroma.Web.PleromaAPI.ChatView do
     }
   end
 
-  def render("index.json", %{chats: chats}) do
-    render_many(chats, __MODULE__, "show.json")
+  def render("index.json", %{chats: chats} = opts) do
+    render_many(chats, __MODULE__, "show.json", Map.delete(opts, :chats))
+  end
+
+  defp account_view_opts(opts, recipient) do
+    account_view_opts = Map.put(opts, :user, recipient)
+
+    if Map.has_key?(account_view_opts, :for) do
+      account_view_opts
+    else
+      Map.put(account_view_opts, :skip_visibility_check, true)
+    end
   end
 end
index 84d2d303dae6223a9c53a8f48a72d38d64cd6c91..e0f98b50a754ba94536c1d6d6428c5f72046be61 100644 (file)
@@ -17,7 +17,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionView do
     %{
       name: emoji,
       count: length(users),
-      accounts: render(AccountView, "index.json", users: users, for: user, as: :user),
+      accounts: render(AccountView, "index.json", users: users, for: user),
       me: !!(user && user.ap_id in user_ap_ids)
     }
   end
index 1729141e996bba5fc9de39846da505390e311f0a..747f2dc6be5a2e22832b364c7ed93f15b4aaf512 100644 (file)
@@ -11,10 +11,10 @@ defmodule Pleroma.Web.RichMedia.Helpers do
 
   @spec validate_page_url(URI.t() | binary()) :: :ok | :error
   defp validate_page_url(page_url) when is_binary(page_url) do
-    validate_tld = Application.get_env(:auto_linker, :opts)[:validate_tld]
+    validate_tld = Pleroma.Config.get([Pleroma.Formatter, :validate_tld])
 
     page_url
-    |> AutoLinker.Parser.url?(scheme: true, validate_tld: validate_tld)
+    |> Linkify.Parser.url?(validate_tld: validate_tld)
     |> parse_uri(page_url)
   end
 
index 386308362b6baaf295b70932e3270846cfce2b2f..c6433cc5325f3e923dc9150632c82ec5945d7d5a 100644 (file)
@@ -138,6 +138,7 @@ defmodule Pleroma.Web.Router do
     patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
     patch("/users/activate", AdminAPIController, :user_activate)
     patch("/users/deactivate", AdminAPIController, :user_deactivate)
+    patch("/users/approve", AdminAPIController, :user_approve)
     put("/users/tag", AdminAPIController, :tag_users)
     delete("/users/tag", AdminAPIController, :untag_users)
 
index 5cfb385ac3068e84e36de42f7026abfeff5005a9..2294d9d0dd82f800cc9e88c383d6a19beee3c330 100644 (file)
@@ -19,6 +19,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
       |> Map.put(:nickname, params[:username])
       |> Map.put(:name, Map.get(params, :fullname, params[:username]))
       |> Map.put(:password_confirmation, params[:password])
+      |> Map.put(:registration_reason, params[:reason])
 
     if Pleroma.Config.get([:instance, :registrations_open]) do
       create_user(params, opts)
@@ -44,6 +45,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
 
     case User.register(changeset) do
       {:ok, user} ->
+        maybe_notify_admins(user)
         {:ok, user}
 
       {:error, changeset} ->
@@ -56,6 +58,18 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
     end
   end
 
+  defp maybe_notify_admins(%User{} = account) do
+    if Pleroma.Config.get([:instance, :account_approval_required]) do
+      User.all_superusers()
+      |> Enum.filter(fn user -> not is_nil(user.email) end)
+      |> Enum.each(fn superuser ->
+        superuser
+        |> Pleroma.Emails.AdminEmail.new_unapproved_registration(account)
+        |> Pleroma.Emails.Mailer.deliver_async()
+      end)
+    end
+  end
+
   def password_reset(nickname_or_email) do
     with true <- is_binary(nickname_or_email),
          %User{local: true, email: email} = user when is_binary(email) <-
index f739dacb61221722a3c53f744529fb9fa11d2a27..b1669d1986809d98b9985405f82e5e7f6ab021b7 100644 (file)
@@ -9,36 +9,6 @@ defmodule Pleroma.Web.MastoFEView do
   alias Pleroma.Web.MastodonAPI.AccountView
   alias Pleroma.Web.MastodonAPI.CustomEmojiView
 
-  @default_settings %{
-    onboarded: true,
-    home: %{
-      shows: %{
-        reblog: true,
-        reply: true
-      }
-    },
-    notifications: %{
-      alerts: %{
-        follow: true,
-        favourite: true,
-        reblog: true,
-        mention: true
-      },
-      shows: %{
-        follow: true,
-        favourite: true,
-        reblog: true,
-        mention: true
-      },
-      sounds: %{
-        follow: true,
-        favourite: true,
-        reblog: true,
-        mention: true
-      }
-    }
-  }
-
   def initial_state(token, user, custom_emojis) do
     limit = Config.get([:instance, :limit])
 
@@ -86,7 +56,7 @@ defmodule Pleroma.Web.MastoFEView do
           "video\/mp4"
         ]
       },
-      settings: user.mastofe_settings || @default_settings,
+      settings: user.mastofe_settings || %{},
       push_subscription: nil,
       accounts: %{user.id => render(AccountView, "show.json", user: user, for: user)},
       custom_emojis: render(CustomEmojiView, "index.json", custom_emojis: custom_emojis),
diff --git a/mix.exs b/mix.exs
index 52b4cf26840b460fb387ec2670f0a02c7dbfad0c..a14b0c51a350fea0bff83b69056792a1f1219ca0 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -145,18 +145,19 @@ defmodule Pleroma.Mixfile do
       {:ex_aws, "~> 2.1"},
       {:ex_aws_s3, "~> 2.0"},
       {:sweet_xml, "~> 0.6.6"},
-      {:earmark, "~> 1.3"},
+      {:earmark, "1.4.3"},
       {:bbcode_pleroma, "~> 0.2.0"},
       {:ex_machina, "~> 2.3", only: :test},
       {:credo, "~> 1.1.0", only: [:dev, :test], runtime: false},
       {:mock, "~> 0.3.3", only: :test},
       {:crypt,
-       git: "https://github.com/msantos/crypt", ref: "f63a705f92c26955977ee62a313012e309a4d77a"},
+       git: "https://github.com/msantos/crypt.git",
+       ref: "f63a705f92c26955977ee62a313012e309a4d77a"},
       {:cors_plug, "~> 1.5"},
       {:ex_doc, "~> 0.21", only: :dev, runtime: false},
       {:web_push_encryption, "~> 0.2.1"},
       {:swoosh,
-       git: "https://github.com/swoosh/swoosh",
+       git: "https://github.com/swoosh/swoosh.git",
        ref: "c96e0ca8a00d8f211ec1f042a4626b09f249caa5",
        override: true},
       {:phoenix_swoosh, "~> 0.2"},
@@ -166,9 +167,7 @@ defmodule Pleroma.Mixfile do
       {:floki, "~> 0.25"},
       {:timex, "~> 3.5"},
       {:ueberauth, "~> 0.4"},
-      {:auto_linker,
-       git: "https://git.pleroma.social/pleroma/auto_linker.git",
-       ref: "95e8188490e97505c56636c1379ffdf036c1fdde"},
+      {:linkify, "~> 0.2.0"},
       {:http_signatures,
        git: "https://git.pleroma.social/pleroma/http_signatures.git",
        ref: "293d77bb6f4a67ac8bde1428735c3b42f22cbb30"},
@@ -190,7 +189,7 @@ defmodule Pleroma.Mixfile do
       {:excoveralls, "~> 0.12.1", only: :test},
       {:flake_id, "~> 0.1.0"},
       {:concurrent_limiter,
-       git: "https://git.pleroma.social/pleroma/elixir-libraries/concurrent_limiter",
+       git: "https://git.pleroma.social/pleroma/elixir-libraries/concurrent_limiter.git",
        ref: "8eee96c6ba39b9286ec44c51c52d9f2758951365"},
       {:remote_ip,
        git: "https://git.pleroma.social/pleroma/remote_ip.git",
index 8dd37a40fc5deb77a483ba0d98b5d2def8929e92..80679cdedd8c3ef316c0833a62d8d6bda2275eef 100644 (file)
--- a/mix.lock
+++ b/mix.lock
@@ -1,6 +1,5 @@
 %{
   "accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm", "11b18c220bcc2eab63b5470c038ef10eb6783bcb1fcdb11aa4137defa5ac1bb8"},
-  "auto_linker": {:git, "https://git.pleroma.social/pleroma/auto_linker.git", "95e8188490e97505c56636c1379ffdf036c1fdde", [ref: "95e8188490e97505c56636c1379ffdf036c1fdde"]},
   "base62": {:hex, :base62, "1.2.1", "4866763e08555a7b3917064e9eef9194c41667276c51b59de2bc42c6ea65f806", [:mix], [{:custom_base, "~> 0.2.1", [hex: :custom_base, repo: "hexpm", optional: false]}], "hexpm", "3b29948de2013d3f93aa898c884a9dff847e7aec75d9d6d8c1dc4c61c2716c42"},
   "base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
   "bbcode": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/bbcode.git", "f2d267675e9a7e1ad1ea9beb4cc23382762b66c2", [ref: "v0.2.0"]},
   "certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "3b3b5f36493004ac3455966991eaf6e768ce9884693d9968055aeeeb1e575040"},
   "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
   "comeonin": {:hex, :comeonin, "5.3.1", "7fe612b739c78c9c1a75186ef2d322ce4d25032d119823269d0aa1e2f1e20025", [:mix], [], "hexpm", "d6222483060c17f0977fad1b7401ef0c5863c985a64352755f366aee3799c245"},
-  "concurrent_limiter": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/concurrent_limiter", "8eee96c6ba39b9286ec44c51c52d9f2758951365", [ref: "8eee96c6ba39b9286ec44c51c52d9f2758951365"]},
+  "concurrent_limiter": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/concurrent_limiter.git", "8eee96c6ba39b9286ec44c51c52d9f2758951365", [ref: "8eee96c6ba39b9286ec44c51c52d9f2758951365"]},
   "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"},
   "cors_plug": {:hex, :cors_plug, "1.5.2", "72df63c87e4f94112f458ce9d25800900cc88608c1078f0e4faddf20933eda6e", [:mix], [{:plug, "~> 1.3 or ~> 1.4 or ~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "9af027d20dc12dd0c4345a6b87247e0c62965871feea0bfecf9764648b02cc69"},
   "cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "04fd8c6a39edc6aaa9c26123009200fc61f92a3a94f3178c527b70b767c6e605"},
   "cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm", "79f954a7021b302186a950a32869dbc185523d99d3e44ce430cd1f3289f41ed4"},
   "credo": {:hex, :credo, "1.1.5", "caec7a3cadd2e58609d7ee25b3931b129e739e070539ad1a0cd7efeeb47014f4", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d0bbd3222607ccaaac5c0340f7f525c627ae4d7aee6c8c8c108922620c5b6446"},
   "crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
-  "crypt": {:git, "https://github.com/msantos/crypt", "f63a705f92c26955977ee62a313012e309a4d77a", [ref: "f63a705f92c26955977ee62a313012e309a4d77a"]},
+  "crypt": {:git, "https://github.com/msantos/crypt.git", "f63a705f92c26955977ee62a313012e309a4d77a", [ref: "f63a705f92c26955977ee62a313012e309a4d77a"]},
   "custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"},
   "db_connection": {:hex, :db_connection, "2.2.2", "3bbca41b199e1598245b716248964926303b5d4609ff065125ce98bcd368939e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "642af240d8a8affb93b4ba5a6fcd2bbcbdc327e1a524b825d383711536f8070c"},
   "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"},
@@ -63,6 +62,7 @@
   "jose": {:hex, :jose, "1.10.1", "16d8e460dae7203c6d1efa3f277e25b5af8b659febfc2f2eb4bacf87f128b80a", [:mix, :rebar3], [], "hexpm", "3c7ddc8a9394b92891db7c2771da94bf819834a1a4c92e30857b7d582e2f8257"},
   "jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
   "libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"},
+  "linkify": {:hex, :linkify, "0.2.0", "2518bbbea21d2caa9d372424e1ad845b640c6630e2d016f1bd1f518f9ebcca28", [:mix], [], "hexpm", "b8ca8a68b79e30b7938d6c996085f3db14939f29538a59ca5101988bb7f917f6"},
   "makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "a10c6eb62cca416019663129699769f0c2ccf39428b3bb3c0cb38c718a0c186d"},
   "makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "d4b316c7222a85bbaa2fd7c6e90e37e953257ad196dc229505137c5e505e9eff"},
   "meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm", "d34f013c156db51ad57cc556891b9720e6a1c1df5fe2e15af999c84d6cebeb1a"},
   "sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"},
   "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
   "sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"},
-  "swoosh": {:git, "https://github.com/swoosh/swoosh", "c96e0ca8a00d8f211ec1f042a4626b09f249caa5", [ref: "c96e0ca8a00d8f211ec1f042a4626b09f249caa5"]},
+  "swoosh": {:git, "https://github.com/swoosh/swoosh.git", "c96e0ca8a00d8f211ec1f042a4626b09f249caa5", [ref: "c96e0ca8a00d8f211ec1f042a4626b09f249caa5"]},
   "syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"},
   "telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"},
   "tesla": {:git, "https://github.com/teamon/tesla.git", "af3707078b10793f6a534938e56b963aff82fe3c", [ref: "af3707078b10793f6a534938e56b963aff82fe3c"]},
diff --git a/priv/repo/migrations/20200712234852_add_approval_fields_to_users.exs b/priv/repo/migrations/20200712234852_add_approval_fields_to_users.exs
new file mode 100644 (file)
index 0000000..43f741a
--- /dev/null
@@ -0,0 +1,10 @@
+defmodule Pleroma.Repo.Migrations.AddApprovalFieldsToUsers do
+  use Ecto.Migration
+
+  def change do
+    alter table(:users) do
+      add(:approval_pending, :boolean)
+      add(:registration_reason, :text)
+    end
+  end
+end
diff --git a/priv/repo/migrations/20200716195806_autolinker_to_linkify.exs b/priv/repo/migrations/20200716195806_autolinker_to_linkify.exs
new file mode 100644 (file)
index 0000000..570acba
--- /dev/null
@@ -0,0 +1,36 @@
+defmodule Pleroma.Repo.Migrations.AutolinkerToLinkify do
+  use Ecto.Migration
+  alias Pleroma.ConfigDB
+
+  @autolinker_path %{group: :auto_linker, key: :opts}
+  @linkify_path %{group: :pleroma, key: Pleroma.Formatter}
+
+  @compat_opts [:class, :rel, :new_window, :truncate, :strip_prefix, :extra]
+
+  def change do
+    with {:ok, {old, new}} <- maybe_get_params() do
+      move_config(old, new)
+    end
+  end
+
+  defp move_config(%{} = old, %{} = new) do
+    {:ok, _} = ConfigDB.update_or_create(new)
+    {:ok, _} = ConfigDB.delete(old)
+    :ok
+  end
+
+  defp maybe_get_params() do
+    with %ConfigDB{value: opts} <- ConfigDB.get_by_params(@autolinker_path),
+         opts <- transform_opts(opts),
+         %{} = linkify_params <- Map.put(@linkify_path, :value, opts) do
+      {:ok, {@autolinker_path, linkify_params}}
+    end
+  end
+
+  def transform_opts(opts) when is_list(opts) do
+    opts
+    |> Enum.into(%{})
+    |> Map.take(@compat_opts)
+    |> Map.to_list()
+  end
+end
diff --git a/priv/repo/migrations/20200722185515_fix_malformed_formatter_config.exs b/priv/repo/migrations/20200722185515_fix_malformed_formatter_config.exs
new file mode 100644 (file)
index 0000000..77b7608
--- /dev/null
@@ -0,0 +1,26 @@
+defmodule Pleroma.Repo.Migrations.FixMalformedFormatterConfig do
+  use Ecto.Migration
+  alias Pleroma.ConfigDB
+
+  @config_path %{group: :pleroma, key: Pleroma.Formatter}
+
+  def change do
+    with %ConfigDB{value: %{} = opts} <- ConfigDB.get_by_params(@config_path),
+         fixed_opts <- Map.to_list(opts) do
+      fix_config(fixed_opts)
+    else
+      _ -> :skipped
+    end
+  end
+
+  defp fix_config(fixed_opts) when is_list(fixed_opts) do
+    {:ok, _} =
+      ConfigDB.update_or_create(%{
+        group: :pleroma,
+        key: Pleroma.Formatter,
+        value: fixed_opts
+      })
+
+    :ok
+  end
+end
diff --git a/priv/repo/migrations/20200724133313_move_welcome_settings.exs b/priv/repo/migrations/20200724133313_move_welcome_settings.exs
new file mode 100644 (file)
index 0000000..323a8fc
--- /dev/null
@@ -0,0 +1,94 @@
+defmodule Pleroma.Repo.Migrations.MoveWelcomeSettings do
+  use Ecto.Migration
+
+  alias Pleroma.ConfigDB
+
+  @old_keys [:welcome_user_nickname, :welcome_message]
+
+  def up do
+    with {:ok, config, {keep_values, move_values}} <- get_old_values() do
+      insert_welcome_settings(move_values)
+      update_instance_config(config, keep_values)
+    end
+  end
+
+  def down do
+    with {:ok, welcome_config, revert_values} <- get_revert_values() do
+      revert_instance_config(revert_values)
+      Pleroma.Repo.delete(welcome_config)
+    end
+  end
+
+  defp insert_welcome_settings([_ | _] = values) do
+    unless String.trim(values[:welcome_message]) == "" do
+      config_values = [
+        direct_message: %{
+          enabled: true,
+          sender_nickname: values[:welcome_user_nickname],
+          message: values[:welcome_message]
+        },
+        email: %{
+          enabled: false,
+          sender: nil,
+          subject: "Welcome to <%= instance_name %>",
+          html: "Welcome to <%= instance_name %>",
+          text: "Welcome to <%= instance_name %>"
+        }
+      ]
+
+      {:ok, _} =
+        %ConfigDB{}
+        |> ConfigDB.changeset(%{group: :pleroma, key: :welcome, value: config_values})
+        |> Pleroma.Repo.insert()
+    end
+
+    :ok
+  end
+
+  defp insert_welcome_settings(_), do: :noop
+
+  defp revert_instance_config(%{} = revert_values) do
+    values = [
+      welcome_user_nickname: revert_values[:sender_nickname],
+      welcome_message: revert_values[:message]
+    ]
+
+    ConfigDB.update_or_create(%{group: :pleroma, key: :instance, value: values})
+  end
+
+  defp revert_instance_config(_), do: :noop
+
+  defp update_instance_config(config, values) do
+    {:ok, _} =
+      config
+      |> ConfigDB.changeset(%{value: values})
+      |> Pleroma.Repo.update()
+
+    :ok
+  end
+
+  defp get_revert_values do
+    config = ConfigDB.get_by_params(%{group: :pleroma, key: :welcome})
+
+    cond do
+      is_nil(config) -> {:noop, nil, nil}
+      true -> {:ok, config, config.value[:direct_message]}
+    end
+  end
+
+  defp get_old_values do
+    config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
+
+    cond do
+      is_nil(config) ->
+        {:noop, config, {}}
+
+      is_binary(config.value[:welcome_message]) ->
+        {:ok, config,
+         {Keyword.drop(config.value, @old_keys), Keyword.take(config.value, @old_keys)}}
+
+      true ->
+        {:ok, config, {Keyword.drop(config.value, @old_keys), []}}
+    end
+  end
+end
index 481cdfd73e6217e8b7900dfcd082fa0fa6951534..21d24ddd069514d2aa67f3855e9ecc21ddddd79a 100644 (file)
@@ -9,6 +9,56 @@ defmodule Pleroma.ApplicationRequirementsTest do
 
   alias Pleroma.Repo
 
+  describe "check_welcome_message_config!/1" do
+    setup do: clear_config([:welcome])
+    setup do: clear_config([Pleroma.Emails.Mailer])
+
+    test "raises if welcome email enabled but mail disabled" do
+      Pleroma.Config.put([:welcome, :email, :enabled], true)
+      Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false)
+
+      assert_raise Pleroma.ApplicationRequirements.VerifyError, "The mail disabled.", fn ->
+        capture_log(&Pleroma.ApplicationRequirements.verify!/0)
+      end
+    end
+  end
+
+  describe "check_confirmation_accounts!" do
+    setup_with_mocks([
+      {Pleroma.ApplicationRequirements, [:passthrough],
+       [
+         check_migrations_applied!: fn _ -> :ok end
+       ]}
+    ]) do
+      :ok
+    end
+
+    setup do: clear_config([:instance, :account_activation_required])
+
+    test "raises if account confirmation is required but mailer isn't enable" do
+      Pleroma.Config.put([:instance, :account_activation_required], true)
+      Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false)
+
+      assert_raise Pleroma.ApplicationRequirements.VerifyError,
+                   "Account activation enabled, but Mailer is disabled. Cannot send confirmation emails.",
+                   fn ->
+                     capture_log(&Pleroma.ApplicationRequirements.verify!/0)
+                   end
+    end
+
+    test "doesn't do anything if account confirmation is disabled" do
+      Pleroma.Config.put([:instance, :account_activation_required], false)
+      Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false)
+      assert Pleroma.ApplicationRequirements.verify!() == :ok
+    end
+
+    test "doesn't do anything if account confirmation is required and mailer is enabled" do
+      Pleroma.Config.put([:instance, :account_activation_required], true)
+      Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], true)
+      assert Pleroma.ApplicationRequirements.verify!() == :ok
+    end
+  end
+
   describe "check_rum!" do
     setup_with_mocks([
       {Pleroma.ApplicationRequirements, [:passthrough],
index 9082ae5a7c2a7eaabdff516b2ab7f74d43e013a2..e24231e270553340075277cca73edb8cf4e5c2bd 100644 (file)
@@ -46,4 +46,24 @@ defmodule Pleroma.Emails.AdminEmailTest do
     assert res.to == [{to_user.name, to_user.email}]
     assert res.from == {config[:name], config[:notify_email]}
   end
+
+  test "new unapproved registration email" do
+    config = Pleroma.Config.get(:instance)
+    to_user = insert(:user)
+    account = insert(:user, registration_reason: "Plz let me in")
+
+    res = AdminEmail.new_unapproved_registration(to_user, account)
+
+    account_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, account.id)
+
+    assert res.to == [{to_user.name, to_user.email}]
+    assert res.from == {config[:name], config[:notify_email]}
+    assert res.subject == "New account up for review on #{config[:name]} (@#{account.nickname})"
+
+    assert res.html_body == """
+           <p>New account for review: <a href="#{account_url}">@#{account.nickname}</a></p>
+           <blockquote>Plz let me in</blockquote>
+           <a href="http://localhost:4001/pleroma/admin">Visit AdminFE</a>
+           """
+  end
 end
index bef5a2c28813b045cf8ba07cdc9ae84691140d65..f066bd50aeb598b94ceb368e84eb0f246e0c9bd6 100644 (file)
@@ -10,6 +10,7 @@ defmodule Pleroma.FormatterTest do
   import Pleroma.Factory
 
   setup_all do
+    clear_config(Pleroma.Formatter)
     Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
     :ok
   end
@@ -255,6 +256,36 @@ defmodule Pleroma.FormatterTest do
 
       assert {_text, ^expected_mentions, []} = Formatter.linkify(text)
     end
+
+    test "it parses URL containing local mention" do
+      _user = insert(:user, %{nickname: "lain"})
+
+      text = "https://example.com/@lain"
+
+      expected = ~S(<a href="https://example.com/@lain" rel="ugc">https://example.com/@lain</a>)
+
+      assert {^expected, [], []} = Formatter.linkify(text)
+    end
+
+    test "it correctly parses angry face D:< with mention" do
+      lain =
+        insert(:user, %{
+          nickname: "lain@lain.com",
+          ap_id: "https://lain.com/users/lain",
+          id: "9qrWmR0cKniB0YU0TA"
+        })
+
+      text = "@lain@lain.com D:<"
+
+      expected_text =
+        ~S(<span class="h-card"><a class="u-url mention" data-user="9qrWmR0cKniB0YU0TA" href="https://lain.com/users/lain" rel="ugc">@<span>lain</span></a></span> D:<)
+
+      expected_mentions = [
+        {"@lain@lain.com", lain}
+      ]
+
+      assert {^expected_text, ^expected_mentions, []} = Formatter.linkify(text)
+    end
   end
 
   describe ".parse_tags" do
diff --git a/test/migrations/20200716195806_autolinker_to_linkify_test.exs b/test/migrations/20200716195806_autolinker_to_linkify_test.exs
new file mode 100644 (file)
index 0000000..250d11c
--- /dev/null
@@ -0,0 +1,68 @@
+defmodule Pleroma.Repo.Migrations.AutolinkerToLinkifyTest do
+  use Pleroma.DataCase
+  import Pleroma.Factory
+  import Pleroma.Tests.Helpers
+  alias Pleroma.ConfigDB
+
+  setup do: clear_config(Pleroma.Formatter)
+  setup_all do: require_migration("20200716195806_autolinker_to_linkify")
+
+  test "change/0 converts auto_linker opts for Pleroma.Formatter", %{migration: migration} do
+    autolinker_opts = [
+      extra: true,
+      validate_tld: true,
+      class: false,
+      strip_prefix: false,
+      new_window: false,
+      rel: "testing"
+    ]
+
+    insert(:config, group: :auto_linker, key: :opts, value: autolinker_opts)
+
+    migration.change()
+
+    assert nil == ConfigDB.get_by_params(%{group: :auto_linker, key: :opts})
+
+    %{value: new_opts} = ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Formatter})
+
+    assert new_opts == [
+             class: false,
+             extra: true,
+             new_window: false,
+             rel: "testing",
+             strip_prefix: false
+           ]
+
+    Pleroma.Config.put(Pleroma.Formatter, new_opts)
+    assert new_opts == Pleroma.Config.get(Pleroma.Formatter)
+
+    {text, _mentions, []} =
+      Pleroma.Formatter.linkify(
+        "https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7\n\nOmg will COVID finally end Black Friday???"
+      )
+
+    assert text ==
+             "<a href=\"https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7\" rel=\"testing\">https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7</a>\n\nOmg will COVID finally end Black Friday???"
+  end
+
+  test "transform_opts/1 returns a list of compatible opts", %{migration: migration} do
+    old_opts = [
+      extra: true,
+      validate_tld: true,
+      class: false,
+      strip_prefix: false,
+      new_window: false,
+      rel: "qqq"
+    ]
+
+    expected_opts = [
+      class: false,
+      extra: true,
+      new_window: false,
+      rel: "qqq",
+      strip_prefix: false
+    ]
+
+    assert migration.transform_opts(old_opts) == expected_opts
+  end
+end
diff --git a/test/migrations/20200722185515_fix_malformed_formatter_config_test.exs b/test/migrations/20200722185515_fix_malformed_formatter_config_test.exs
new file mode 100644 (file)
index 0000000..d349047
--- /dev/null
@@ -0,0 +1,66 @@
+defmodule Pleroma.Repo.Migrations.FixMalformedFormatterConfigTest do
+  use Pleroma.DataCase
+  import Pleroma.Factory
+  import Pleroma.Tests.Helpers
+  alias Pleroma.ConfigDB
+
+  setup do: clear_config(Pleroma.Formatter)
+  setup_all do: require_migration("20200722185515_fix_malformed_formatter_config")
+
+  test "change/0 converts a map into a list", %{migration: migration} do
+    incorrect_opts = %{
+      class: false,
+      extra: true,
+      new_window: false,
+      rel: "F",
+      strip_prefix: false
+    }
+
+    insert(:config, group: :pleroma, key: Pleroma.Formatter, value: incorrect_opts)
+
+    assert :ok == migration.change()
+
+    %{value: new_opts} = ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Formatter})
+
+    assert new_opts == [
+             class: false,
+             extra: true,
+             new_window: false,
+             rel: "F",
+             strip_prefix: false
+           ]
+
+    Pleroma.Config.put(Pleroma.Formatter, new_opts)
+    assert new_opts == Pleroma.Config.get(Pleroma.Formatter)
+
+    {text, _mentions, []} =
+      Pleroma.Formatter.linkify(
+        "https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7\n\nOmg will COVID finally end Black Friday???"
+      )
+
+    assert text ==
+             "<a href=\"https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7\" rel=\"F\">https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7</a>\n\nOmg will COVID finally end Black Friday???"
+  end
+
+  test "change/0 skips if Pleroma.Formatter config is already a list", %{migration: migration} do
+    opts = [
+      class: false,
+      extra: true,
+      new_window: false,
+      rel: "ugc",
+      strip_prefix: false
+    ]
+
+    insert(:config, group: :pleroma, key: Pleroma.Formatter, value: opts)
+
+    assert :skipped == migration.change()
+
+    %{value: new_opts} = ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Formatter})
+
+    assert new_opts == opts
+  end
+
+  test "change/0 skips if Pleroma.Formatter is empty", %{migration: migration} do
+    assert :skipped == migration.change()
+  end
+end
diff --git a/test/migrations/20200724133313_move_welcome_settings_test.exs b/test/migrations/20200724133313_move_welcome_settings_test.exs
new file mode 100644 (file)
index 0000000..739f245
--- /dev/null
@@ -0,0 +1,140 @@
+defmodule Pleroma.Repo.Migrations.MoveWelcomeSettingsTest do
+  use Pleroma.DataCase
+  import Pleroma.Factory
+  import Pleroma.Tests.Helpers
+  alias Pleroma.ConfigDB
+
+  setup_all do: require_migration("20200724133313_move_welcome_settings")
+
+  describe "up/0" do
+    test "converts welcome settings", %{migration: migration} do
+      insert(:config,
+        group: :pleroma,
+        key: :instance,
+        value: [
+          welcome_message: "Test message",
+          welcome_user_nickname: "jimm",
+          name: "Pleroma"
+        ]
+      )
+
+      migration.up()
+      instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
+      welcome_config = ConfigDB.get_by_params(%{group: :pleroma, key: :welcome})
+
+      assert instance_config.value == [name: "Pleroma"]
+
+      assert welcome_config.value == [
+               direct_message: %{
+                 enabled: true,
+                 message: "Test message",
+                 sender_nickname: "jimm"
+               },
+               email: %{
+                 enabled: false,
+                 html: "Welcome to <%= instance_name %>",
+                 sender: nil,
+                 subject: "Welcome to <%= instance_name %>",
+                 text: "Welcome to <%= instance_name %>"
+               }
+             ]
+    end
+
+    test "does nothing when message empty", %{migration: migration} do
+      insert(:config,
+        group: :pleroma,
+        key: :instance,
+        value: [
+          welcome_message: "",
+          welcome_user_nickname: "jimm",
+          name: "Pleroma"
+        ]
+      )
+
+      migration.up()
+      instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
+      refute ConfigDB.get_by_params(%{group: :pleroma, key: :welcome})
+      assert instance_config.value == [name: "Pleroma"]
+    end
+
+    test "does nothing when welcome_message not set", %{migration: migration} do
+      insert(:config,
+        group: :pleroma,
+        key: :instance,
+        value: [welcome_user_nickname: "jimm", name: "Pleroma"]
+      )
+
+      migration.up()
+      instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
+      refute ConfigDB.get_by_params(%{group: :pleroma, key: :welcome})
+      assert instance_config.value == [name: "Pleroma"]
+    end
+  end
+
+  describe "down/0" do
+    test "revert new settings to old when instance setting not exists", %{migration: migration} do
+      insert(:config,
+        group: :pleroma,
+        key: :welcome,
+        value: [
+          direct_message: %{
+            enabled: true,
+            message: "Test message",
+            sender_nickname: "jimm"
+          },
+          email: %{
+            enabled: false,
+            html: "Welcome to <%= instance_name %>",
+            sender: nil,
+            subject: "Welcome to <%= instance_name %>",
+            text: "Welcome to <%= instance_name %>"
+          }
+        ]
+      )
+
+      migration.down()
+
+      refute ConfigDB.get_by_params(%{group: :pleroma, key: :welcome})
+      instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
+
+      assert instance_config.value == [
+               welcome_user_nickname: "jimm",
+               welcome_message: "Test message"
+             ]
+    end
+
+    test "revert new settings to old when instance setting exists", %{migration: migration} do
+      insert(:config, group: :pleroma, key: :instance, value: [name: "Pleroma App"])
+
+      insert(:config,
+        group: :pleroma,
+        key: :welcome,
+        value: [
+          direct_message: %{
+            enabled: true,
+            message: "Test message",
+            sender_nickname: "jimm"
+          },
+          email: %{
+            enabled: false,
+            html: "Welcome to <%= instance_name %>",
+            sender: nil,
+            subject: "Welcome to <%= instance_name %>",
+            text: "Welcome to <%= instance_name %>"
+          }
+        ]
+      )
+
+      migration.down()
+
+      refute ConfigDB.get_by_params(%{group: :pleroma, key: :welcome})
+      instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
+
+      assert instance_config.value == [
+               name: "Pleroma App",
+               welcome_user_nickname: "jimm",
+               welcome_message: "Test message"
+             ]
+    end
+  end
+end
diff --git a/test/plugs/frontend_static_test.exs b/test/plugs/frontend_static_test.exs
new file mode 100644 (file)
index 0000000..d11d91d
--- /dev/null
@@ -0,0 +1,30 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.FrontendStaticPlugTest do
+  use Pleroma.Web.ConnCase
+
+  @dir "test/tmp/instance_static"
+
+  setup do
+    File.mkdir_p!(@dir)
+    on_exit(fn -> File.rm_rf(@dir) end)
+  end
+
+  setup do: clear_config([:instance, :static_dir], @dir)
+
+  test "overrides existing static files", %{conn: conn} do
+    name = "pelmora"
+    ref = "uguu"
+
+    clear_config([:frontends, :primary], %{"name" => name, "ref" => ref})
+    path = "#{@dir}/frontends/#{name}/#{ref}"
+
+    File.mkdir_p!(path)
+    File.write!("#{path}/index.html", "from frontend plug")
+
+    index = get(conn, "/")
+    assert html_response(index, 200) == "from frontend plug"
+  end
+end
index be2613ad098ce0907345679811639017a7a91b30..d42ba817efd87366e412f21b07a7dbb98fd11c4e 100644 (file)
@@ -2,7 +2,7 @@
 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
 # SPDX-License-Identifier: AGPL-3.0-only
 
-defmodule Pleroma.Web.RuntimeStaticPlugTest do
+defmodule Pleroma.Web.InstanceStaticPlugTest do
   use Pleroma.Web.ConnCase
 
   @dir "test/tmp/instance_static"
@@ -24,6 +24,28 @@ defmodule Pleroma.Web.RuntimeStaticPlugTest do
     assert html_response(index, 200) == "hello world"
   end
 
+  test "also overrides frontend files", %{conn: conn} do
+    name = "pelmora"
+    ref = "uguu"
+
+    clear_config([:frontends, :primary], %{"name" => name, "ref" => ref})
+
+    bundled_index = get(conn, "/")
+    refute html_response(bundled_index, 200) == "from frontend plug"
+
+    path = "#{@dir}/frontends/#{name}/#{ref}"
+    File.mkdir_p!(path)
+    File.write!("#{path}/index.html", "from frontend plug")
+
+    index = get(conn, "/")
+    assert html_response(index, 200) == "from frontend plug"
+
+    File.write!(@dir <> "/index.html", "from instance static")
+
+    index = get(conn, "/")
+    assert html_response(index, 200) == "from instance static"
+  end
+
   test "overrides any file in static/static" do
     bundled_index = get(build_conn(), "/static/terms-of-service.html")
 
index 26281b45e74dd5a9d5bba84321b755d590a70bf9..5cbf2e29197c14de391dcef39aea3126ef3401d3 100644 (file)
@@ -32,6 +32,11 @@ defmodule Pleroma.Tests.Helpers do
     end
   end
 
+  def require_migration(migration_name) do
+    [{module, _}] = Code.require_file("#{migration_name}.exs", "priv/repo/migrations")
+    {:ok, %{migration: module}}
+  end
+
   defmacro __using__(_opts) do
     quote do
       import Pleroma.Tests.Helpers,
index 71f36c0e362c2fce32380079a40313cae3fc5f13..fb12e7fb32efe692cfa38133bec260fdaa7b51a1 100644 (file)
@@ -129,8 +129,6 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
           autofollowed_nicknames: [],
           max_pinned_statuses: 1,
           attachment_links: false,
-          welcome_user_nickname: nil,
-          welcome_message: nil,
           max_report_comment_size: 1000,
           safe_dm_mentions: false,
           healthcheck: false,
@@ -172,7 +170,7 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
         end
 
       assert file ==
-               "#{header}\n\nconfig :pleroma, :instance,\n  name: \"Pleroma\",\n  email: \"example@example.com\",\n  notify_email: \"noreply@example.com\",\n  description: \"A Pleroma instance, an alternative fediverse server\",\n  limit: 5000,\n  chat_limit: 5000,\n  remote_limit: 100_000,\n  upload_limit: 16_000_000,\n  avatar_upload_limit: 2_000_000,\n  background_upload_limit: 4_000_000,\n  banner_upload_limit: 4_000_000,\n  poll_limits: %{\n    max_expiration: 31_536_000,\n    max_option_chars: 200,\n    max_options: 20,\n    min_expiration: 0\n  },\n  registrations_open: true,\n  federating: true,\n  federation_incoming_replies_max_depth: 100,\n  federation_reachability_timeout_days: 7,\n  federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher],\n  allow_relay: true,\n  public: true,\n  quarantined_instances: [],\n  managed_config: true,\n  static_dir: \"instance/static/\",\n  allowed_post_formats: [\"text/plain\", \"text/html\", \"text/markdown\", \"text/bbcode\"],\n  autofollowed_nicknames: [],\n  max_pinned_statuses: 1,\n  attachment_links: false,\n  welcome_user_nickname: nil,\n  welcome_message: nil,\n  max_report_comment_size: 1000,\n  safe_dm_mentions: false,\n  healthcheck: false,\n  remote_post_retention_days: 90,\n  skip_thread_containment: true,\n  limit_to_local_content: :unauthenticated,\n  user_bio_length: 5000,\n  user_name_length: 100,\n  max_account_fields: 10,\n  max_remote_account_fields: 20,\n  account_field_name_length: 512,\n  account_field_value_length: 2048,\n  external_user_synchronization: true,\n  extended_nickname_format: true,\n  multi_factor_authentication: [\n    totp: [digits: 6, period: 30],\n    backup_codes: [number: 2, length: 6]\n  ]\n"
+               "#{header}\n\nconfig :pleroma, :instance,\n  name: \"Pleroma\",\n  email: \"example@example.com\",\n  notify_email: \"noreply@example.com\",\n  description: \"A Pleroma instance, an alternative fediverse server\",\n  limit: 5000,\n  chat_limit: 5000,\n  remote_limit: 100_000,\n  upload_limit: 16_000_000,\n  avatar_upload_limit: 2_000_000,\n  background_upload_limit: 4_000_000,\n  banner_upload_limit: 4_000_000,\n  poll_limits: %{\n    max_expiration: 31_536_000,\n    max_option_chars: 200,\n    max_options: 20,\n    min_expiration: 0\n  },\n  registrations_open: true,\n  federating: true,\n  federation_incoming_replies_max_depth: 100,\n  federation_reachability_timeout_days: 7,\n  federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher],\n  allow_relay: true,\n  public: true,\n  quarantined_instances: [],\n  managed_config: true,\n  static_dir: \"instance/static/\",\n  allowed_post_formats: [\"text/plain\", \"text/html\", \"text/markdown\", \"text/bbcode\"],\n  autofollowed_nicknames: [],\n  max_pinned_statuses: 1,\n  attachment_links: false,\n  max_report_comment_size: 1000,\n  safe_dm_mentions: false,\n  healthcheck: false,\n  remote_post_retention_days: 90,\n  skip_thread_containment: true,\n  limit_to_local_content: :unauthenticated,\n  user_bio_length: 5000,\n  user_name_length: 100,\n  max_account_fields: 10,\n  max_remote_account_fields: 20,\n  account_field_name_length: 512,\n  account_field_value_length: 2048,\n  external_user_synchronization: true,\n  extended_nickname_format: true,\n  multi_factor_authentication: [\n    totp: [digits: 6, period: 30],\n    backup_codes: [number: 2, length: 6]\n  ]\n"
     end
   end
 end
diff --git a/test/user/welcome_email_test.exs b/test/user/welcome_email_test.exs
new file mode 100644 (file)
index 0000000..d005d11
--- /dev/null
@@ -0,0 +1,61 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.User.WelcomeEmailTest do
+  use Pleroma.DataCase
+
+  alias Pleroma.Config
+  alias Pleroma.Tests.ObanHelpers
+  alias Pleroma.User.WelcomeEmail
+
+  import Pleroma.Factory
+  import Swoosh.TestAssertions
+
+  setup do: clear_config([:welcome])
+
+  describe "send_email/1" do
+    test "send a welcome email" do
+      user = insert(:user, name: "Jimm")
+
+      Config.put([:welcome, :email, :enabled], true)
+      Config.put([:welcome, :email, :sender], "welcome@pleroma.app")
+
+      Config.put(
+        [:welcome, :email, :subject],
+        "Hello, welcome to pleroma: <%= instance_name %>"
+      )
+
+      Config.put(
+        [:welcome, :email, :html],
+        "<h1>Hello <%= user.name %>.</h1> <p>Welcome to <%= instance_name %></p>"
+      )
+
+      instance_name = Config.get([:instance, :name])
+
+      {:ok, _job} = WelcomeEmail.send_email(user)
+
+      ObanHelpers.perform_all()
+
+      assert_email_sent(
+        from: {instance_name, "welcome@pleroma.app"},
+        to: {user.name, user.email},
+        subject: "Hello, welcome to pleroma: #{instance_name}",
+        html_body: "<h1>Hello #{user.name}.</h1> <p>Welcome to #{instance_name}</p>"
+      )
+
+      Config.put([:welcome, :email, :sender], {"Pleroma App", "welcome@pleroma.app"})
+
+      {:ok, _job} = WelcomeEmail.send_email(user)
+
+      ObanHelpers.perform_all()
+
+      assert_email_sent(
+        from: {"Pleroma App", "welcome@pleroma.app"},
+        to: {user.name, user.email},
+        subject: "Hello, welcome to pleroma: #{instance_name}",
+        html_body: "<h1>Hello #{user.name}.</h1> <p>Welcome to #{instance_name}</p>"
+      )
+    end
+  end
+end
diff --git a/test/user/welcome_message_test.exs b/test/user/welcome_message_test.exs
new file mode 100644 (file)
index 0000000..3cd6f5c
--- /dev/null
@@ -0,0 +1,34 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.User.WelcomeMessageTest do
+  use Pleroma.DataCase
+
+  alias Pleroma.Config
+  alias Pleroma.User.WelcomeMessage
+
+  import Pleroma.Factory
+
+  setup do: clear_config([:welcome])
+
+  describe "post_message/1" do
+    test "send a direct welcome message" do
+      welcome_user = insert(:user)
+      user = insert(:user, name: "Jimm")
+
+      Config.put([:welcome, :direct_message, :enabled], true)
+      Config.put([:welcome, :direct_message, :sender_nickname], welcome_user.nickname)
+
+      Config.put(
+        [:welcome, :direct_message, :message],
+        "Hello. Welcome to Pleroma"
+      )
+
+      {:ok, %Pleroma.Activity{} = activity} = WelcomeMessage.post_message(user)
+      assert user.ap_id in activity.recipients
+      assert activity.data["directMessage"] == true
+      assert Pleroma.Object.normalize(activity).data["content"] =~ "Hello. Welcome to Pleroma"
+    end
+  end
+end
index 9788e09d9b24f5d74bace103993e53c6c6d5797a..624baf8ad15d323356a9fa767137de9a5e7afe5f 100644 (file)
@@ -17,6 +17,7 @@ defmodule Pleroma.UserTest do
 
   import Pleroma.Factory
   import ExUnit.CaptureLog
+  import Swoosh.TestAssertions
 
   setup_all do
     Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
@@ -385,9 +386,10 @@ defmodule Pleroma.UserTest do
       password_confirmation: "test",
       email: "email@example.com"
     }
+
     setup do: clear_config([:instance, :autofollowed_nicknames])
-    setup do: clear_config([:instance, :welcome_message])
-    setup do: clear_config([:instance, :welcome_user_nickname])
+    setup do: clear_config([:welcome])
+    setup do: clear_config([:instance, :account_activation_required])
 
     test "it autofollows accounts that are set for it" do
       user = insert(:user)
@@ -408,20 +410,45 @@ defmodule Pleroma.UserTest do
 
     test "it sends a welcome message if it is set" do
       welcome_user = insert(:user)
+      Pleroma.Config.put([:welcome, :direct_message, :enabled], true)
+      Pleroma.Config.put([:welcome, :direct_message, :sender_nickname], welcome_user.nickname)
+      Pleroma.Config.put([:welcome, :direct_message, :message], "Hello, this is a cool site")
+
+      Pleroma.Config.put([:welcome, :email, :enabled], true)
+      Pleroma.Config.put([:welcome, :email, :sender], welcome_user.email)
+
+      Pleroma.Config.put(
+        [:welcome, :email, :subject],
+        "Hello, welcome to cool site: <%= instance_name %>"
+      )
 
-      Pleroma.Config.put([:instance, :welcome_user_nickname], welcome_user.nickname)
-      Pleroma.Config.put([:instance, :welcome_message], "Hello, this is a cool site")
+      instance_name = Pleroma.Config.get([:instance, :name])
 
       cng = User.register_changeset(%User{}, @full_user_data)
       {:ok, registered_user} = User.register(cng)
+      ObanHelpers.perform_all()
 
       activity = Repo.one(Pleroma.Activity)
       assert registered_user.ap_id in activity.recipients
       assert Object.normalize(activity).data["content"] =~ "cool site"
       assert activity.actor == welcome_user.ap_id
+
+      assert_email_sent(
+        from: {instance_name, welcome_user.email},
+        to: {registered_user.name, registered_user.email},
+        subject: "Hello, welcome to cool site: #{instance_name}",
+        html_body: "Welcome to #{instance_name}"
+      )
     end
 
-    setup do: clear_config([:instance, :account_activation_required])
+    test "it sends a confirm email" do
+      Pleroma.Config.put([:instance, :account_activation_required], true)
+
+      cng = User.register_changeset(%User{}, @full_user_data)
+      {:ok, registered_user} = User.register(cng)
+      ObanHelpers.perform_all()
+      assert_email_sent(Pleroma.Emails.UserEmail.account_confirmation_email(registered_user))
+    end
 
     test "it requires an email, name, nickname and password, bio is optional when account_activation_required is enabled" do
       Pleroma.Config.put([:instance, :account_activation_required], true)
@@ -516,6 +543,46 @@ defmodule Pleroma.UserTest do
     end
   end
 
+  describe "user registration, with :account_approval_required" do
+    @full_user_data %{
+      bio: "A guy",
+      name: "my name",
+      nickname: "nick",
+      password: "test",
+      password_confirmation: "test",
+      email: "email@example.com",
+      registration_reason: "I'm a cool guy :)"
+    }
+    setup do: clear_config([:instance, :account_approval_required], true)
+
+    test "it creates unapproved user" do
+      changeset = User.register_changeset(%User{}, @full_user_data)
+      assert changeset.valid?
+
+      {:ok, user} = Repo.insert(changeset)
+
+      assert user.approval_pending
+      assert user.registration_reason == "I'm a cool guy :)"
+    end
+
+    test "it restricts length of registration reason" do
+      reason_limit = Pleroma.Config.get([:instance, :registration_reason_length])
+
+      assert is_integer(reason_limit)
+
+      params =
+        @full_user_data
+        |> Map.put(
+          :registration_reason,
+          "Quia et nesciunt dolores numquam ipsam nisi sapiente soluta. Ullam repudiandae nisi quam porro officiis officiis ad. Consequatur animi velit ex quia. Odit voluptatem perferendis quia ut nisi. Dignissimos sit soluta atque aliquid dolorem ut dolorum ut. Labore voluptates iste iusto amet voluptatum earum. Ad fugit illum nam eos ut nemo. Pariatur ea fuga non aspernatur. Dignissimos debitis officia corporis est nisi ab et. Atque itaque alias eius voluptas minus. Accusamus numquam tempore occaecati in."
+        )
+
+      changeset = User.register_changeset(%User{}, params)
+
+      refute changeset.valid?
+    end
+  end
+
   describe "get_or_fetch/1" do
     test "gets an existing user by nickname" do
       user = insert(:user)
@@ -1181,6 +1248,31 @@ defmodule Pleroma.UserTest do
     end
   end
 
+  describe "approve" do
+    test "approves a user" do
+      user = insert(:user, approval_pending: true)
+      assert true == user.approval_pending
+      {:ok, user} = User.approve(user)
+      assert false == user.approval_pending
+    end
+
+    test "approves a list of users" do
+      unapproved_users = [
+        insert(:user, approval_pending: true),
+        insert(:user, approval_pending: true),
+        insert(:user, approval_pending: true)
+      ]
+
+      {:ok, users} = User.approve(unapproved_users)
+
+      assert Enum.count(users) == 3
+
+      Enum.each(users, fn user ->
+        assert false == user.approval_pending
+      end)
+    end
+  end
+
   describe "delete" do
     setup do
       {:ok, user} = insert(:user) |> User.set_cache()
@@ -1268,6 +1360,17 @@ defmodule Pleroma.UserTest do
     end
   end
 
+  test "delete/1 when approval is pending deletes the user" do
+    user = insert(:user, approval_pending: true)
+    {:ok, user: user}
+
+    {:ok, job} = User.delete(user)
+    {:ok, _} = ObanHelpers.perform(job)
+
+    refute User.get_cached_by_id(user.id)
+    refute User.get_by_id(user.id)
+  end
+
   test "get_public_key_for_ap_id fetches a user that's not in the db" do
     assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
   end
@@ -1342,6 +1445,14 @@ defmodule Pleroma.UserTest do
       user = insert(:user, local: true, confirmation_pending: false, deactivated: true)
       assert User.account_status(user) == :deactivated
     end
+
+    test "returns :approval_pending for unapproved user" do
+      user = insert(:user, local: true, approval_pending: true)
+      assert User.account_status(user) == :approval_pending
+
+      user = insert(:user, local: true, confirmation_pending: true, approval_pending: true)
+      assert User.account_status(user) == :approval_pending
+    end
   end
 
   describe "superuser?/1" do
index f3951462f8d459c4e6fcafeeaf07f778ae9d4bba..d6eab7337c7022f1fb7056d596a8fc9e45ad9ced 100644 (file)
@@ -1179,7 +1179,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
         "id" => activity_ap_id,
         "content" => content,
         "published" => activity_with_object.object.data["published"],
-        "actor" => AccountView.render("show.json", %{user: target_account})
+        "actor" =>
+          AccountView.render("show.json", %{user: target_account, skip_visibility_check: true})
       }
 
       assert %Activity{
index 38ddec5bb7e8f4ff9275a032936326ed0b1ed4c3..9a283f27d6b3330532e06ed5fd525a56f022eb96 100644 (file)
@@ -78,5 +78,15 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrependedTest do
       assert {:ok, res} = EnsureRePrepended.filter(message)
       assert res == message
     end
+
+    test "it skips if the object is only a reference" do
+      message = %{
+        "type" => "Create",
+        "object" => "somereference"
+      }
+
+      assert {:ok, res} = EnsureRePrepended.filter(message)
+      assert res == message
+    end
   end
 end
index 8deb64501380858e083e6757bd5d03a2e6391469..f2a231eaf1780e86d9433ccbc846f06eb5ee637f 100644 (file)
@@ -14,6 +14,51 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do
       :ok
     end
 
+    test "when given an `object_data` in meta, Federation will receive a the original activity with the `object` field set to this embedded object" do
+      activity = insert(:note_activity)
+      object = %{"id" => "1", "type" => "Love"}
+      meta = [local: true, object_data: object]
+
+      activity_with_object = %{activity | data: Map.put(activity.data, "object", object)}
+
+      with_mocks([
+        {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]},
+        {
+          Pleroma.Web.ActivityPub.MRF,
+          [],
+          [filter: fn o -> {:ok, o} end]
+        },
+        {
+          Pleroma.Web.ActivityPub.ActivityPub,
+          [],
+          [persist: fn o, m -> {:ok, o, m} end]
+        },
+        {
+          Pleroma.Web.ActivityPub.SideEffects,
+          [],
+          [
+            handle: fn o, m -> {:ok, o, m} end,
+            handle_after_transaction: fn m -> m end
+          ]
+        },
+        {
+          Pleroma.Web.Federator,
+          [],
+          [publish: fn _o -> :ok end]
+        }
+      ]) do
+        assert {:ok, ^activity, ^meta} =
+                 Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta)
+
+        assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta))
+        assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity))
+        assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta))
+        assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
+        refute called(Pleroma.Web.Federator.publish(activity))
+        assert_called(Pleroma.Web.Federator.publish(activity_with_object))
+      end
+    end
+
     test "it goes through validation, filtering, persisting, side effects and federation for local activities" do
       activity = insert(:note_activity)
       meta = [local: true]
index 2649b060ab53f0881a4473670f50e64b83101b12..4a08eb7ee4c36329423e201b7794db9b242e126d 100644 (file)
@@ -312,8 +312,12 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
       }
     end
 
-    test "deletes the original block", %{block_undo: block_undo, block: block} do
-      {:ok, _block_undo, _} = SideEffects.handle(block_undo)
+    test "deletes the original block", %{
+      block_undo: block_undo,
+      block: block
+    } do
+      {:ok, _block_undo, _meta} = SideEffects.handle(block_undo)
+
       refute Activity.get_by_id(block.id)
     end
 
index 248b410c641cebb290746f5279d932922102a3a3..7d33feaf28176a4fff62159854ae960671f1fddc 100644 (file)
@@ -160,7 +160,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       assert capture_log(fn ->
                {:ok, _returned_activity} = Transmogrifier.handle_incoming(data)
-             end) =~ "[error] Couldn't fetch \"https://404.site/whatever\", error: nil"
+             end) =~ "[warn] Couldn't fetch \"https://404.site/whatever\", error: nil"
     end
 
     test "it works for incoming notices" do
@@ -710,7 +710,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
         "id" => activity.data["id"],
         "content" => "test post",
         "published" => object.data["published"],
-        "actor" => AccountView.render("show.json", %{user: user})
+        "actor" => AccountView.render("show.json", %{user: user, skip_visibility_check: true})
       }
 
       message = %{
index 361dc5a41e3da9ca631630fad2d718dd173f2f0e..d5021354595756eeb7d4f20b546db953d622857a 100644 (file)
@@ -482,7 +482,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
         "id" => activity_ap_id,
         "content" => content,
         "published" => activity.object.data["published"],
-        "actor" => AccountView.render("show.json", %{user: target_account})
+        "actor" =>
+          AccountView.render("show.json", %{user: target_account, skip_visibility_check: true})
       }
 
       assert %{
index da91cd552afb8552695baa21f861c84d3c3d9666..b5d5bd8c70e837bc6ca09825174a7c24cdf0ba5e 100644 (file)
@@ -9,6 +9,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
   import ExUnit.CaptureLog
   import Mock
   import Pleroma.Factory
+  import Swoosh.TestAssertions
 
   alias Pleroma.Activity
   alias Pleroma.Config
@@ -348,7 +349,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
         "avatar" => User.avatar_url(user) |> MediaProxy.url(),
         "display_name" => HTML.strip_tags(user.name || user.nickname),
         "confirmation_pending" => false,
-        "url" => user.ap_id
+        "approval_pending" => false,
+        "url" => user.ap_id,
+        "registration_reason" => nil
       }
 
       assert expected == json_response(conn, 200)
@@ -612,6 +615,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
   describe "GET /api/pleroma/admin/users" do
     test "renders users array for the first page", %{conn: conn, admin: admin} do
       user = insert(:user, local: false, tags: ["foo", "bar"])
+      user2 = insert(:user, approval_pending: true, registration_reason: "I'm a chill dude")
+
       conn = get(conn, "/api/pleroma/admin/users?page=1")
 
       users =
@@ -626,7 +631,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
             "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
             "display_name" => HTML.strip_tags(admin.name || admin.nickname),
             "confirmation_pending" => false,
-            "url" => admin.ap_id
+            "approval_pending" => false,
+            "url" => admin.ap_id,
+            "registration_reason" => nil
           },
           %{
             "deactivated" => user.deactivated,
@@ -638,13 +645,29 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
             "avatar" => User.avatar_url(user) |> MediaProxy.url(),
             "display_name" => HTML.strip_tags(user.name || user.nickname),
             "confirmation_pending" => false,
-            "url" => user.ap_id
+            "approval_pending" => false,
+            "url" => user.ap_id,
+            "registration_reason" => nil
+          },
+          %{
+            "deactivated" => user2.deactivated,
+            "id" => user2.id,
+            "nickname" => user2.nickname,
+            "roles" => %{"admin" => false, "moderator" => false},
+            "local" => true,
+            "tags" => [],
+            "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
+            "display_name" => HTML.strip_tags(user2.name || user2.nickname),
+            "confirmation_pending" => false,
+            "approval_pending" => true,
+            "url" => user2.ap_id,
+            "registration_reason" => "I'm a chill dude"
           }
         ]
         |> Enum.sort_by(& &1["nickname"])
 
       assert json_response(conn, 200) == %{
-               "count" => 2,
+               "count" => 3,
                "page_size" => 50,
                "users" => users
              }
@@ -711,7 +734,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    "avatar" => User.avatar_url(user) |> MediaProxy.url(),
                    "display_name" => HTML.strip_tags(user.name || user.nickname),
                    "confirmation_pending" => false,
-                   "url" => user.ap_id
+                   "approval_pending" => false,
+                   "url" => user.ap_id,
+                   "registration_reason" => nil
                  }
                ]
              }
@@ -737,7 +762,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    "avatar" => User.avatar_url(user) |> MediaProxy.url(),
                    "display_name" => HTML.strip_tags(user.name || user.nickname),
                    "confirmation_pending" => false,
-                   "url" => user.ap_id
+                   "approval_pending" => false,
+                   "url" => user.ap_id,
+                   "registration_reason" => nil
                  }
                ]
              }
@@ -763,7 +790,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    "avatar" => User.avatar_url(user) |> MediaProxy.url(),
                    "display_name" => HTML.strip_tags(user.name || user.nickname),
                    "confirmation_pending" => false,
-                   "url" => user.ap_id
+                   "approval_pending" => false,
+                   "url" => user.ap_id,
+                   "registration_reason" => nil
                  }
                ]
              }
@@ -789,7 +818,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    "avatar" => User.avatar_url(user) |> MediaProxy.url(),
                    "display_name" => HTML.strip_tags(user.name || user.nickname),
                    "confirmation_pending" => false,
-                   "url" => user.ap_id
+                   "approval_pending" => false,
+                   "url" => user.ap_id,
+                   "registration_reason" => nil
                  }
                ]
              }
@@ -815,7 +846,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    "avatar" => User.avatar_url(user) |> MediaProxy.url(),
                    "display_name" => HTML.strip_tags(user.name || user.nickname),
                    "confirmation_pending" => false,
-                   "url" => user.ap_id
+                   "approval_pending" => false,
+                   "url" => user.ap_id,
+                   "registration_reason" => nil
                  }
                ]
              }
@@ -841,7 +874,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    "avatar" => User.avatar_url(user) |> MediaProxy.url(),
                    "display_name" => HTML.strip_tags(user.name || user.nickname),
                    "confirmation_pending" => false,
-                   "url" => user.ap_id
+                   "approval_pending" => false,
+                   "url" => user.ap_id,
+                   "registration_reason" => nil
                  }
                ]
              }
@@ -862,7 +897,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
                    "display_name" => HTML.strip_tags(user2.name || user2.nickname),
                    "confirmation_pending" => false,
-                   "url" => user2.ap_id
+                   "approval_pending" => false,
+                   "url" => user2.ap_id,
+                   "registration_reason" => nil
                  }
                ]
              }
@@ -895,7 +932,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    "avatar" => User.avatar_url(user) |> MediaProxy.url(),
                    "display_name" => HTML.strip_tags(user.name || user.nickname),
                    "confirmation_pending" => false,
-                   "url" => user.ap_id
+                   "approval_pending" => false,
+                   "url" => user.ap_id,
+                   "registration_reason" => nil
                  }
                ]
              }
@@ -921,7 +960,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
             "avatar" => User.avatar_url(user) |> MediaProxy.url(),
             "display_name" => HTML.strip_tags(user.name || user.nickname),
             "confirmation_pending" => false,
-            "url" => user.ap_id
+            "approval_pending" => false,
+            "url" => user.ap_id,
+            "registration_reason" => nil
           },
           %{
             "deactivated" => admin.deactivated,
@@ -933,7 +974,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
             "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
             "display_name" => HTML.strip_tags(admin.name || admin.nickname),
             "confirmation_pending" => false,
-            "url" => admin.ap_id
+            "approval_pending" => false,
+            "url" => admin.ap_id,
+            "registration_reason" => nil
           },
           %{
             "deactivated" => false,
@@ -945,7 +988,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
             "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
             "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
             "confirmation_pending" => false,
-            "url" => old_admin.ap_id
+            "approval_pending" => false,
+            "url" => old_admin.ap_id,
+            "registration_reason" => nil
           }
         ]
         |> Enum.sort_by(& &1["nickname"])
@@ -957,6 +1002,44 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
              }
     end
 
+    test "only unapproved users", %{conn: conn} do
+      user =
+        insert(:user,
+          nickname: "sadboy",
+          approval_pending: true,
+          registration_reason: "Plz let me in!"
+        )
+
+      insert(:user, nickname: "happyboy", approval_pending: false)
+
+      conn = get(conn, "/api/pleroma/admin/users?filters=need_approval")
+
+      users =
+        [
+          %{
+            "deactivated" => user.deactivated,
+            "id" => user.id,
+            "nickname" => user.nickname,
+            "roles" => %{"admin" => false, "moderator" => false},
+            "local" => true,
+            "tags" => [],
+            "avatar" => User.avatar_url(user) |> MediaProxy.url(),
+            "display_name" => HTML.strip_tags(user.name || user.nickname),
+            "confirmation_pending" => false,
+            "approval_pending" => true,
+            "url" => user.ap_id,
+            "registration_reason" => "Plz let me in!"
+          }
+        ]
+        |> Enum.sort_by(& &1["nickname"])
+
+      assert json_response(conn, 200) == %{
+               "count" => 1,
+               "page_size" => 50,
+               "users" => users
+             }
+    end
+
     test "load only admins", %{conn: conn, admin: admin} do
       second_admin = insert(:user, is_admin: true)
       insert(:user)
@@ -976,7 +1059,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
             "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
             "display_name" => HTML.strip_tags(admin.name || admin.nickname),
             "confirmation_pending" => false,
-            "url" => admin.ap_id
+            "approval_pending" => false,
+            "url" => admin.ap_id,
+            "registration_reason" => nil
           },
           %{
             "deactivated" => false,
@@ -988,7 +1073,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
             "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
             "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
             "confirmation_pending" => false,
-            "url" => second_admin.ap_id
+            "approval_pending" => false,
+            "url" => second_admin.ap_id,
+            "registration_reason" => nil
           }
         ]
         |> Enum.sort_by(& &1["nickname"])
@@ -1021,7 +1108,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
                    "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
                    "confirmation_pending" => false,
-                   "url" => moderator.ap_id
+                   "approval_pending" => false,
+                   "url" => moderator.ap_id,
+                   "registration_reason" => nil
                  }
                ]
              }
@@ -1047,7 +1136,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
             "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
             "display_name" => HTML.strip_tags(user1.name || user1.nickname),
             "confirmation_pending" => false,
-            "url" => user1.ap_id
+            "approval_pending" => false,
+            "url" => user1.ap_id,
+            "registration_reason" => nil
           },
           %{
             "deactivated" => false,
@@ -1059,7 +1150,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
             "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
             "display_name" => HTML.strip_tags(user2.name || user2.nickname),
             "confirmation_pending" => false,
-            "url" => user2.ap_id
+            "approval_pending" => false,
+            "url" => user2.ap_id,
+            "registration_reason" => nil
           }
         ]
         |> Enum.sort_by(& &1["nickname"])
@@ -1099,7 +1192,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    "avatar" => User.avatar_url(user) |> MediaProxy.url(),
                    "display_name" => HTML.strip_tags(user.name || user.nickname),
                    "confirmation_pending" => false,
-                   "url" => user.ap_id
+                   "approval_pending" => false,
+                   "url" => user.ap_id,
+                   "registration_reason" => nil
                  }
                ]
              }
@@ -1124,7 +1219,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
                    "display_name" => HTML.strip_tags(admin.name || admin.nickname),
                    "confirmation_pending" => false,
-                   "url" => admin.ap_id
+                   "approval_pending" => false,
+                   "url" => admin.ap_id,
+                   "registration_reason" => nil
                  }
                ]
              }
@@ -1171,6 +1268,26 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
              "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
   end
 
+  test "PATCH /api/pleroma/admin/users/approve", %{admin: admin, conn: conn} do
+    user_one = insert(:user, approval_pending: true)
+    user_two = insert(:user, approval_pending: true)
+
+    conn =
+      patch(
+        conn,
+        "/api/pleroma/admin/users/approve",
+        %{nicknames: [user_one.nickname, user_two.nickname]}
+      )
+
+    response = json_response(conn, 200)
+    assert Enum.map(response["users"], & &1["approval_pending"]) == [false, false]
+
+    log_entry = Repo.one(ModerationLog)
+
+    assert ModerationLog.get_log_entry_message(log_entry) ==
+             "@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}"
+  end
+
   test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
     user = insert(:user)
 
@@ -1187,7 +1304,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "avatar" => User.avatar_url(user) |> MediaProxy.url(),
                "display_name" => HTML.strip_tags(user.name || user.nickname),
                "confirmation_pending" => false,
-               "url" => user.ap_id
+               "approval_pending" => false,
+               "url" => user.ap_id,
+               "registration_reason" => nil
              }
 
     log_entry = Repo.one(ModerationLog)
@@ -1731,6 +1850,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
                  second_user.nickname
                }"
+
+      ObanHelpers.perform_all()
+      assert_email_sent(Pleroma.Emails.UserEmail.account_confirmation_email(first_user))
     end
   end
 
index e0e3d4153895d04966665674916682b4de522154..b974cedd5a2dd7ce11566bef41b76a0534c2036e 100644 (file)
@@ -166,5 +166,16 @@ defmodule Pleroma.Web.AdminAPI.SearchTest do
       assert total == 3
       assert count == 1
     end
+
+    test "it returns unapproved user" do
+      unapproved = insert(:user, approval_pending: true)
+      insert(:user)
+      insert(:user)
+
+      {:ok, _results, total} = Search.user()
+      {:ok, [^unapproved], count} = Search.user(%{need_approval: true})
+      assert total == 3
+      assert count == 1
+    end
   end
 end
index f00b0afb2a71786e6fee2521daf0c0f5cbd4943a..5a02292be77a1d66ee621c77d27db13187fa3a7f 100644 (file)
@@ -4,11 +4,14 @@
 
 defmodule Pleroma.Web.AdminAPI.ReportViewTest do
   use Pleroma.DataCase
+
   import Pleroma.Factory
+
+  alias Pleroma.Web.AdminAPI
   alias Pleroma.Web.AdminAPI.Report
   alias Pleroma.Web.AdminAPI.ReportView
   alias Pleroma.Web.CommonAPI
-  alias Pleroma.Web.MastodonAPI.AccountView
+  alias Pleroma.Web.MastodonAPI
   alias Pleroma.Web.MastodonAPI.StatusView
 
   test "renders a report" do
@@ -21,13 +24,16 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do
       content: nil,
       actor:
         Map.merge(
-          AccountView.render("show.json", %{user: user}),
-          Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user})
+          MastodonAPI.AccountView.render("show.json", %{user: user, skip_visibility_check: true}),
+          AdminAPI.AccountView.render("show.json", %{user: user})
         ),
       account:
         Map.merge(
-          AccountView.render("show.json", %{user: other_user}),
-          Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: other_user})
+          MastodonAPI.AccountView.render("show.json", %{
+            user: other_user,
+            skip_visibility_check: true
+          }),
+          AdminAPI.AccountView.render("show.json", %{user: other_user})
         ),
       statuses: [],
       notes: [],
@@ -56,13 +62,16 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do
       content: nil,
       actor:
         Map.merge(
-          AccountView.render("show.json", %{user: user}),
-          Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user})
+          MastodonAPI.AccountView.render("show.json", %{user: user, skip_visibility_check: true}),
+          AdminAPI.AccountView.render("show.json", %{user: user})
         ),
       account:
         Map.merge(
-          AccountView.render("show.json", %{user: other_user}),
-          Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: other_user})
+          MastodonAPI.AccountView.render("show.json", %{
+            user: other_user,
+            skip_visibility_check: true
+          }),
+          AdminAPI.AccountView.render("show.json", %{user: other_user})
         ),
       statuses: [StatusView.render("show.json", %{activity: activity})],
       state: "open",
index 7e11fede3d8247563bfe38a7ae7bbc94a56971f3..313dda21b491006786b01aa2beef24b62e476fc8 100644 (file)
@@ -624,14 +624,27 @@ defmodule Pleroma.Web.CommonAPITest do
       user = insert(:user)
       other_user = insert(:user)
 
-      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
-      {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
+      clear_config([:instance, :federating], true)
+
+      with_mock Pleroma.Web.Federator,
+        publish: fn _ -> nil end do
+        {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
+        {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
+
+        {:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
 
-      {:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
+        assert unreaction.data["type"] == "Undo"
+        assert unreaction.data["object"] == reaction.data["id"]
+        assert unreaction.local
 
-      assert unreaction.data["type"] == "Undo"
-      assert unreaction.data["object"] == reaction.data["id"]
-      assert unreaction.local
+        # On federation, it contains the undone (and deleted) object
+        unreaction_with_object = %{
+          unreaction
+          | data: Map.put(unreaction.data, "object", reaction.data)
+        }
+
+        assert called(Pleroma.Web.Federator.publish(unreaction_with_object))
+      end
     end
 
     test "repeating a status" do
index fa2ed1ea55bd370073ae12262a32833895848ade..0d2a619674d053039c8df113126f95fc9e75985f 100644 (file)
@@ -181,6 +181,17 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
 
       assert activity_titles == ['public', 'unlisted']
     end
+
+    test "returns 404 when the user is remote", %{conn: conn} do
+      user = insert(:user, local: false)
+
+      {:ok, _} = CommonAPI.post(user, %{status: "test"})
+
+      assert conn
+             |> put_req_header("accept", "application/atom+xml")
+             |> get(user_feed_path(conn, :feed, user.nickname))
+             |> response(404)
+    end
   end
 
   # Note: see ActivityPubControllerTest for JSON format tests
index c304487eae581373957b7c04ee475ee627c0ee7d..e6b283aab390790074cb911678363b60f21038fa 100644 (file)
@@ -904,6 +904,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
     end
 
     setup do: clear_config([:instance, :account_activation_required])
+    setup do: clear_config([:instance, :account_approval_required])
 
     test "Account registration via Application", %{conn: conn} do
       conn =
@@ -968,6 +969,75 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
       assert token_from_db.user.confirmation_pending
     end
 
+    test "Account registration via app with account_approval_required", %{conn: conn} do
+      Pleroma.Config.put([:instance, :account_approval_required], true)
+
+      conn =
+        conn
+        |> put_req_header("content-type", "application/json")
+        |> post("/api/v1/apps", %{
+          client_name: "client_name",
+          redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
+          scopes: "read, write, follow"
+        })
+
+      assert %{
+               "client_id" => client_id,
+               "client_secret" => client_secret,
+               "id" => _,
+               "name" => "client_name",
+               "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
+               "vapid_key" => _,
+               "website" => nil
+             } = json_response_and_validate_schema(conn, 200)
+
+      conn =
+        post(conn, "/oauth/token", %{
+          grant_type: "client_credentials",
+          client_id: client_id,
+          client_secret: client_secret
+        })
+
+      assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
+               json_response(conn, 200)
+
+      assert token
+      token_from_db = Repo.get_by(Token, token: token)
+      assert token_from_db
+      assert refresh
+      assert scope == "read write follow"
+
+      conn =
+        build_conn()
+        |> put_req_header("content-type", "multipart/form-data")
+        |> put_req_header("authorization", "Bearer " <> token)
+        |> post("/api/v1/accounts", %{
+          username: "lain",
+          email: "lain@example.org",
+          password: "PlzDontHackLain",
+          bio: "Test Bio",
+          agreement: true,
+          reason: "I'm a cool dude, bro"
+        })
+
+      %{
+        "access_token" => token,
+        "created_at" => _created_at,
+        "scope" => ^scope,
+        "token_type" => "Bearer"
+      } = json_response_and_validate_schema(conn, 200)
+
+      token_from_db = Repo.get_by(Token, token: token)
+      assert token_from_db
+      token_from_db = Repo.preload(token_from_db, :user)
+      assert token_from_db.user
+
+      assert token_from_db.user.confirmation_pending
+      assert token_from_db.user.approval_pending
+
+      assert token_from_db.user.registration_reason == "I'm a cool dude, bro"
+    end
+
     test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
       _user = insert(:user, email: "lain@example.org")
       app_token = insert(:oauth_token, user: nil)
index 01a24afcf2225a35589b65b1c7aff63dd49648a4..66465450003c213770b0b7ffa583eeee019b9d75 100644 (file)
@@ -32,6 +32,38 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do
     refute User.blocks?(user, other_user)
   end
 
+  test "blocking a domain via query params" do
+    %{user: user, conn: conn} = oauth_access(["write:blocks"])
+    other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
+
+    ret_conn =
+      conn
+      |> put_req_header("content-type", "application/json")
+      |> post("/api/v1/domain_blocks?domain=dogwhistle.zone")
+
+    assert %{} == json_response_and_validate_schema(ret_conn, 200)
+    user = User.get_cached_by_ap_id(user.ap_id)
+    assert User.blocks?(user, other_user)
+  end
+
+  test "unblocking a domain via query params" do
+    %{user: user, conn: conn} = oauth_access(["write:blocks"])
+    other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
+
+    User.block_domain(user, "dogwhistle.zone")
+    user = refresh_record(user)
+    assert User.blocks?(user, other_user)
+
+    ret_conn =
+      conn
+      |> put_req_header("content-type", "application/json")
+      |> delete("/api/v1/domain_blocks?domain=dogwhistle.zone")
+
+    assert %{} == json_response_and_validate_schema(ret_conn, 200)
+    user = User.get_cached_by_ap_id(user.ap_id)
+    refute User.blocks?(user, other_user)
+  end
+
   test "getting a list of domain blocks" do
     %{user: user, conn: conn} = oauth_access(["read:blocks"])
 
index cc880d82ca4ab814e73cb04debee6d128bf04b65..6a9ccd97983c6f9dc50b2026b0a90045d0cb5007 100644 (file)
@@ -27,6 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do
              "thumbnail" => _,
              "languages" => _,
              "registrations" => _,
+             "approval_required" => _,
              "poll_limits" => _,
              "upload_limit" => _,
              "avatar_upload_limit" => _,
index a83bf90a31cdc57ee0612d515dfe07edeb86a57e..8f37efa3c3e2b8df5fa13bfa7955ac5f898fbb3c 100644 (file)
@@ -95,7 +95,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
       }
     }
 
-    assert expected == AccountView.render("show.json", %{user: user})
+    assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true})
   end
 
   test "Favicon is nil when :instances_favicons is disabled" do
@@ -108,11 +108,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
                favicon:
                  "https://shitposter.club/plugins/Qvitter/img/gnusocial-favicons/favicon-16x16.png"
              }
-           } = AccountView.render("show.json", %{user: user})
+           } = AccountView.render("show.json", %{user: user, skip_visibility_check: true})
 
     Config.put([:instances_favicons, :enabled], false)
 
-    assert %{pleroma: %{favicon: nil}} = AccountView.render("show.json", %{user: user})
+    assert %{pleroma: %{favicon: nil}} =
+             AccountView.render("show.json", %{user: user, skip_visibility_check: true})
   end
 
   test "Represent the user account for the account owner" do
@@ -189,7 +190,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
       }
     }
 
-    assert expected == AccountView.render("show.json", %{user: user})
+    assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true})
   end
 
   test "Represent a Funkwhale channel" do
@@ -198,7 +199,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
         "https://channels.tests.funkwhale.audio/federation/actors/compositions"
       )
 
-    assert represented = AccountView.render("show.json", %{user: user})
+    assert represented =
+             AccountView.render("show.json", %{user: user, skip_visibility_check: true})
+
     assert represented.acct == "compositions@channels.tests.funkwhale.audio"
     assert represented.url == "https://channels.tests.funkwhale.audio/channels/compositions"
   end
@@ -223,6 +226,23 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
     assert expected == AccountView.render("mention.json", %{user: user})
   end
 
+  test "demands :for or :skip_visibility_check option for account rendering" do
+    clear_config([:restrict_unauthenticated, :profiles, :local], false)
+
+    user = insert(:user)
+    user_id = user.id
+
+    assert %{id: ^user_id} = AccountView.render("show.json", %{user: user, for: nil})
+    assert %{id: ^user_id} = AccountView.render("show.json", %{user: user, for: user})
+
+    assert %{id: ^user_id} =
+             AccountView.render("show.json", %{user: user, skip_visibility_check: true})
+
+    assert_raise RuntimeError, ~r/:skip_visibility_check or :for option is required/, fn ->
+      AccountView.render("show.json", %{user: user})
+    end
+  end
+
   describe "relationship" do
     defp test_relationship_rendering(user, other_user, expected_result) do
       opts = %{user: user, target: other_user, relationships: nil}
@@ -336,7 +356,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
 
     assert result.pleroma.settings_store == %{:fe => "test"}
 
-    result = AccountView.render("show.json", %{user: user, with_pleroma_settings: true})
+    result = AccountView.render("show.json", %{user: user, for: nil, with_pleroma_settings: true})
     assert result.pleroma[:settings_store] == nil
 
     result = AccountView.render("show.json", %{user: user, for: user})
@@ -345,13 +365,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
 
   test "doesn't sanitize display names" do
     user = insert(:user, name: "<marquee> username </marquee>")
-    result = AccountView.render("show.json", %{user: user})
+    result = AccountView.render("show.json", %{user: user, skip_visibility_check: true})
     assert result.display_name == "<marquee> username </marquee>"
   end
 
   test "never display nil user follow counts" do
     user = insert(:user, following_count: 0, follower_count: 0)
-    result = AccountView.render("show.json", %{user: user})
+    result = AccountView.render("show.json", %{user: user, skip_visibility_check: true})
 
     assert result.following_count == 0
     assert result.followers_count == 0
@@ -375,7 +395,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
                followers_count: 0,
                following_count: 0,
                pleroma: %{hide_follows_count: true, hide_followers_count: true}
-             } = AccountView.render("show.json", %{user: user})
+             } = AccountView.render("show.json", %{user: user, skip_visibility_check: true})
     end
 
     test "shows when follows/followers are hidden" do
@@ -388,7 +408,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
                followers_count: 1,
                following_count: 1,
                pleroma: %{hide_follows: true, hide_followers: true}
-             } = AccountView.render("show.json", %{user: user})
+             } = AccountView.render("show.json", %{user: user, skip_visibility_check: true})
     end
 
     test "shows actual follower/following count to the account owner" do
@@ -531,7 +551,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
         emoji: %{"joker_smile" => "https://evil.website/society.png"}
       )
 
-    AccountView.render("show.json", %{user: user})
+    AccountView.render("show.json", %{user: user, skip_visibility_check: true})
     |> Enum.all?(fn
       {key, url} when key in [:avatar, :avatar_static, :header, :header_static] ->
         String.starts_with?(url, Pleroma.Web.base_url())
index fa26b3129f273037ae97acaef8bade9c1f3c792f..8703d5ba7689a3cb6fb70c97c2be336715acf61c 100644 (file)
@@ -56,6 +56,23 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
            ]
   end
 
+  test "works correctly with badly formatted emojis" do
+    user = insert(:user)
+    {:ok, activity} = CommonAPI.post(user, %{status: "yo"})
+
+    activity
+    |> Object.normalize(false)
+    |> Object.update_data(%{"reactions" => %{"☕" => [user.ap_id], "x" => 1}})
+
+    activity = Activity.get_by_id(activity.id)
+
+    status = StatusView.render("show.json", activity: activity, for: user)
+
+    assert status[:pleroma][:emoji_reactions] == [
+             %{name: "☕", count: 1, me: true}
+           ]
+  end
+
   test "loads and returns the direct conversation id when given the `with_direct_conversation_id` option" do
     user = insert(:user)
 
@@ -177,7 +194,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
       id: to_string(note.id),
       uri: object_data["id"],
       url: Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, note),
-      account: AccountView.render("show.json", %{user: user}),
+      account: AccountView.render("show.json", %{user: user, skip_visibility_check: true}),
       in_reply_to_id: nil,
       in_reply_to_account_id: nil,
       card: nil,
index d389e4ce053ceee468de3ec6c8e1a747f681bba5..1200126b81dca7b5b45be13b95447dc8eed6d3a2 100644 (file)
@@ -19,7 +19,10 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
     key: "_test",
     signing_salt: "cooldude"
   ]
-  setup do: clear_config([:instance, :account_activation_required])
+  setup do
+    clear_config([:instance, :account_activation_required])
+    clear_config([:instance, :account_approval_required])
+  end
 
   describe "in OAuth consumer mode, " do
     setup do
@@ -995,6 +998,30 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
              }
     end
 
+    test "rejects token exchange for valid credentials belonging to an unapproved user" do
+      password = "testpassword"
+
+      user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password), approval_pending: true)
+
+      refute Pleroma.User.account_status(user) == :active
+
+      app = insert(:oauth_app)
+
+      conn =
+        build_conn()
+        |> post("/oauth/token", %{
+          "grant_type" => "password",
+          "username" => user.nickname,
+          "password" => password,
+          "client_id" => app.client_id,
+          "client_secret" => app.client_secret
+        })
+
+      assert resp = json_response(conn, 403)
+      assert %{"error" => _} = resp
+      refute Map.has_key?(resp, "access_token")
+    end
+
     test "rejects an invalid authorization code" do
       app = insert(:oauth_app)
 
index 82e16741db325b457a61457c83db3a34679fee00..d71e80d03ca37799d3664f9b79068f1730f2d2b8 100644 (file)
@@ -332,5 +332,27 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do
                chat_1.id |> to_string()
              ]
     end
+
+    test "it is not affected by :restrict_unauthenticated setting (issue #1973)", %{
+      conn: conn,
+      user: user
+    } do
+      clear_config([:restrict_unauthenticated, :profiles, :local], true)
+      clear_config([:restrict_unauthenticated, :profiles, :remote], true)
+
+      user2 = insert(:user)
+      user3 = insert(:user, local: false)
+
+      {:ok, _chat_12} = Chat.get_or_create(user.id, user2.ap_id)
+      {:ok, _chat_13} = Chat.get_or_create(user.id, user3.ap_id)
+
+      result =
+        conn
+        |> get("/api/v1/pleroma/chats")
+        |> json_response_and_validate_schema(200)
+
+      account_ids = Enum.map(result, &get_in(&1, ["account", "id"]))
+      assert Enum.sort(account_ids) == Enum.sort([user2.id, user3.id])
+    end
   end
 end
index df58a5eb63a92ee796c95b12cf38e13649619bfc..e113bb15fb679697f89093d45a01866e84f39532 100644 (file)
@@ -14,6 +14,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do
               )
   setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
 
+  setup do: clear_config([:instance, :public], true)
+
   setup do
     admin = insert(:user, is_admin: true)
     token = insert(:oauth_admin_token, user: admin)
@@ -27,6 +29,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do
     {:ok, %{admin_conn: admin_conn}}
   end
 
+  test "GET /api/pleroma/emoji/packs when :public: false", %{conn: conn} do
+    Config.put([:instance, :public], false)
+    conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
+  end
+
   test "GET /api/pleroma/emoji/packs", %{conn: conn} do
     resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
 
index 14eecb1bdcdb626a1372b650862a80020e2c088a..02484b705b7ccd380fd60d41d1eb5b5ea2602ba1 100644 (file)
@@ -26,7 +26,8 @@ defmodule Pleroma.Web.PleromaAPI.ChatViewTest do
 
     assert represented_chat == %{
              id: "#{chat.id}",
-             account: AccountView.render("show.json", user: recipient),
+             account:
+               AccountView.render("show.json", user: recipient, skip_visibility_check: true),
              unread: 0,
              last_message: nil,
              updated_at: Utils.to_masto_date(chat.updated_at)
index 368533292bdc8b4acfb6aeac8ebe8afc70813b19..20a45cb6f94d74caa2f65c6333844576e512a7ff 100644 (file)
@@ -4,11 +4,11 @@
 
 defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
   use Pleroma.DataCase
+  import Pleroma.Factory
   alias Pleroma.Repo
   alias Pleroma.Tests.ObanHelpers
   alias Pleroma.User
   alias Pleroma.UserInviteToken
-  alias Pleroma.Web.MastodonAPI.AccountView
   alias Pleroma.Web.TwitterAPI.TwitterAPI
 
   setup_all do
@@ -27,13 +27,10 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
 
     {:ok, user} = TwitterAPI.register_user(data)
 
-    fetched_user = User.get_cached_by_nickname("lain")
-
-    assert AccountView.render("show.json", %{user: user}) ==
-             AccountView.render("show.json", %{user: fetched_user})
+    assert user == User.get_cached_by_nickname("lain")
   end
 
-  test "it registers a new user with empty string in bio and returns the user." do
+  test "it registers a new user with empty string in bio and returns the user" do
     data = %{
       :username => "lain",
       :email => "lain@wired.jp",
@@ -45,10 +42,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
 
     {:ok, user} = TwitterAPI.register_user(data)
 
-    fetched_user = User.get_cached_by_nickname("lain")
-
-    assert AccountView.render("show.json", %{user: user}) ==
-             AccountView.render("show.json", %{user: fetched_user})
+    assert user == User.get_cached_by_nickname("lain")
   end
 
   test "it sends confirmation email if :account_activation_required is specified in instance config" do
@@ -85,6 +79,42 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
     )
   end
 
+  test "it sends an admin email if :account_approval_required is specified in instance config" do
+    admin = insert(:user, is_admin: true)
+    setting = Pleroma.Config.get([:instance, :account_approval_required])
+
+    unless setting do
+      Pleroma.Config.put([:instance, :account_approval_required], true)
+      on_exit(fn -> Pleroma.Config.put([:instance, :account_approval_required], setting) end)
+    end
+
+    data = %{
+      :username => "lain",
+      :email => "lain@wired.jp",
+      :fullname => "lain iwakura",
+      :bio => "",
+      :password => "bear",
+      :confirm => "bear",
+      :reason => "I love anime"
+    }
+
+    {:ok, user} = TwitterAPI.register_user(data)
+    ObanHelpers.perform_all()
+
+    assert user.approval_pending
+
+    email = Pleroma.Emails.AdminEmail.new_unapproved_registration(admin, user)
+
+    notify_email = Pleroma.Config.get([:instance, :notify_email])
+    instance_name = Pleroma.Config.get([:instance, :name])
+
+    Swoosh.TestAssertions.assert_email_sent(
+      from: {instance_name, notify_email},
+      to: {admin.name, admin.email},
+      html_body: email.html_body
+    )
+  end
+
   test "it registers a new user and parses mentions in the bio" do
     data1 = %{
       :username => "john",
@@ -134,13 +164,10 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
 
       {:ok, user} = TwitterAPI.register_user(data)
 
-      fetched_user = User.get_cached_by_nickname("vinny")
-      invite = Repo.get_by(UserInviteToken, token: invite.token)
+      assert user == User.get_cached_by_nickname("vinny")
 
+      invite = Repo.get_by(UserInviteToken, token: invite.token)
       assert invite.used == true
-
-      assert AccountView.render("show.json", %{user: user}) ==
-               AccountView.render("show.json", %{user: fetched_user})
     end
 
     test "returns error on invalid token" do
@@ -197,10 +224,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
       check_fn = fn invite ->
         data = Map.put(data, :token, invite.token)
         {:ok, user} = TwitterAPI.register_user(data)
-        fetched_user = User.get_cached_by_nickname("vinny")
 
-        assert AccountView.render("show.json", %{user: user}) ==
-                 AccountView.render("show.json", %{user: fetched_user})
+        assert user == User.get_cached_by_nickname("vinny")
       end
 
       {:ok, data: data, check_fn: check_fn}
@@ -260,14 +285,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
       }
 
       {:ok, user} = TwitterAPI.register_user(data)
-      fetched_user = User.get_cached_by_nickname("vinny")
-      invite = Repo.get_by(UserInviteToken, token: invite.token)
+      assert user == User.get_cached_by_nickname("vinny")
 
+      invite = Repo.get_by(UserInviteToken, token: invite.token)
       assert invite.used == true
 
-      assert AccountView.render("show.json", %{user: user}) ==
-               AccountView.render("show.json", %{user: fetched_user})
-
       data = %{
         :username => "GrimReaper",
         :email => "death@reapers.afterlife",
@@ -302,13 +324,10 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
       }
 
       {:ok, user} = TwitterAPI.register_user(data)
-      fetched_user = User.get_cached_by_nickname("vinny")
-      invite = Repo.get_by(UserInviteToken, token: invite.token)
+      assert user == User.get_cached_by_nickname("vinny")
 
+      invite = Repo.get_by(UserInviteToken, token: invite.token)
       refute invite.used
-
-      assert AccountView.render("show.json", %{user: user}) ==
-               AccountView.render("show.json", %{user: fetched_user})
     end
 
     test "error after max uses" do
@@ -327,13 +346,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
       }
 
       {:ok, user} = TwitterAPI.register_user(data)
-      fetched_user = User.get_cached_by_nickname("vinny")
+      assert user == User.get_cached_by_nickname("vinny")
+
       invite = Repo.get_by(UserInviteToken, token: invite.token)
       assert invite.used == true
 
-      assert AccountView.render("show.json", %{user: user}) ==
-               AccountView.render("show.json", %{user: fetched_user})
-
       data = %{
         :username => "GrimReaper",
         :email => "death@reapers.afterlife",