Merge branch 'develop' into feature/store-statuses-data-inside-flag
authorMaxim Filippov <colixer@gmail.com>
Sun, 27 Oct 2019 13:11:25 +0000 (16:11 +0300)
committerMaxim Filippov <colixer@gmail.com>
Sun, 27 Oct 2019 13:11:25 +0000 (16:11 +0300)
197 files changed:
.gitlab-ci.yml
CHANGELOG.md
README.md
benchmarks/load_testing/fetcher.ex [new file with mode: 0644]
benchmarks/load_testing/generator.ex [new file with mode: 0644]
benchmarks/load_testing/helper.ex [new file with mode: 0644]
benchmarks/mix/tasks/pleroma/load_testing.ex [new file with mode: 0644]
config/benchmark.exs [new file with mode: 0644]
config/config.exs
config/description.exs
config/releases.exs
docs/API/admin_api.md
docs/API/pleroma_api.md
docs/configuration/cheatsheet.md
lib/mix/tasks/pleroma/database.ex
lib/mix/tasks/pleroma/emoji.ex
lib/mix/tasks/pleroma/relay.ex
lib/mix/tasks/pleroma/user.ex
lib/pleroma/application.ex
lib/pleroma/conversation.ex
lib/pleroma/conversation/participation.ex
lib/pleroma/daemons/digest_email_daemon.ex
lib/pleroma/emails/user_email.ex
lib/pleroma/formatter.ex
lib/pleroma/marker.ex [new file with mode: 0644]
lib/pleroma/moderation_log.ex
lib/pleroma/notification.ex
lib/pleroma/object.ex
lib/pleroma/object/containment.ex
lib/pleroma/object/fetcher.ex
lib/pleroma/plugs/admin_secret_authentication_plug.ex
lib/pleroma/plugs/oauth_plug.ex
lib/pleroma/plugs/user_enabled_plug.ex
lib/pleroma/plugs/user_is_admin_plug.ex
lib/pleroma/stats.ex
lib/pleroma/upload.ex
lib/pleroma/user.ex
lib/pleroma/user/info.ex [deleted file]
lib/pleroma/user/query.ex
lib/pleroma/user/search.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/activity_pub/activity_pub_controller.ex
lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex
lib/pleroma/web/activity_pub/publisher.ex
lib/pleroma/web/activity_pub/relay.ex
lib/pleroma/web/activity_pub/transmogrifier.ex
lib/pleroma/web/activity_pub/utils.ex
lib/pleroma/web/activity_pub/views/user_view.ex
lib/pleroma/web/activity_pub/visibility.ex
lib/pleroma/web/admin_api/admin_api_controller.ex
lib/pleroma/web/admin_api/views/account_view.ex
lib/pleroma/web/common_api/common_api.ex
lib/pleroma/web/federator/federator.ex
lib/pleroma/web/federator/publisher.ex
lib/pleroma/web/masto_fe_controller.ex
lib/pleroma/web/mastodon_api/controllers/account_controller.ex
lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex
lib/pleroma/web/mastodon_api/controllers/marker_controller.ex [new file with mode: 0644]
lib/pleroma/web/mastodon_api/views/account_view.ex
lib/pleroma/web/mastodon_api/views/marker_view.ex [new file with mode: 0644]
lib/pleroma/web/mastodon_api/views/status_view.ex
lib/pleroma/web/mastodon_api/websocket_handler.ex
lib/pleroma/web/oauth/oauth_controller.ex
lib/pleroma/web/ostatus/activity_representer.ex [deleted file]
lib/pleroma/web/ostatus/feed_representer.ex [deleted file]
lib/pleroma/web/ostatus/handlers/delete_handler.ex [deleted file]
lib/pleroma/web/ostatus/handlers/follow_handler.ex [deleted file]
lib/pleroma/web/ostatus/handlers/note_handler.ex [deleted file]
lib/pleroma/web/ostatus/handlers/unfollow_handler.ex [deleted file]
lib/pleroma/web/ostatus/ostatus.ex [deleted file]
lib/pleroma/web/ostatus/ostatus_controller.ex
lib/pleroma/web/ostatus/user_representer.ex [deleted file]
lib/pleroma/web/pleroma_api/controllers/account_controller.ex
lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex
lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex
lib/pleroma/web/push/impl.ex
lib/pleroma/web/router.ex
lib/pleroma/web/salmon/salmon.ex [deleted file]
lib/pleroma/web/streamer/streamer.ex
lib/pleroma/web/streamer/worker.ex
lib/pleroma/web/templates/feed/feed/feed.xml.eex
lib/pleroma/web/templates/masto_fe/index.html.eex
lib/pleroma/web/twitter_api/twitter_api_controller.ex
lib/pleroma/web/views/masto_fe_view.ex
lib/pleroma/web/web_finger/web_finger.ex
lib/pleroma/web/websub/websub.ex [deleted file]
lib/pleroma/web/websub/websub_client_subscription.ex [deleted file]
lib/pleroma/web/websub/websub_controller.ex [deleted file]
lib/pleroma/web/websub/websub_server_subscription.ex [deleted file]
lib/pleroma/workers/receiver_worker.ex
lib/pleroma/workers/subscriber_worker.ex [deleted file]
mix.exs
priv/repo/migrations/20190710125158_add_following_address_from_source_data.exs
priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs [new file with mode: 0644]
priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs
priv/repo/migrations/20191009154606_add_user_info_columns.exs [new file with mode: 0644]
priv/repo/migrations/20191009154608_copy_users_info_fields_to_users.exs [new file with mode: 0644]
priv/repo/migrations/20191014181019_create_markers.exs [new file with mode: 0644]
priv/repo/migrations/20191017225002_drop_websub_tables.exs [new file with mode: 0644]
priv/repo/migrations/20191026190317_set_not_null_for_activities.exs [new file with mode: 0644]
priv/repo/migrations/20191026190415_set_not_null_for_activity_expirations.exs [new file with mode: 0644]
priv/repo/migrations/20191026190500_set_not_null_for_apps.exs [new file with mode: 0644]
priv/repo/migrations/20191026190533_set_not_null_for_bookmarks.exs [new file with mode: 0644]
priv/repo/migrations/20191026190622_set_not_null_for_config.exs [new file with mode: 0644]
priv/repo/migrations/20191026190712_set_not_null_for_conversation_participation_recipient_ships.exs [new file with mode: 0644]
priv/repo/migrations/20191026190759_set_not_null_for_conversation_participations.exs [new file with mode: 0644]
priv/repo/migrations/20191026190841_set_not_null_for_filters.exs [new file with mode: 0644]
priv/repo/migrations/20191026191023_set_not_null_for_instances.exs [new file with mode: 0644]
priv/repo/migrations/20191026191100_set_not_null_for_lists.exs [new file with mode: 0644]
priv/repo/migrations/20191026191134_set_not_null_for_markers.exs [new file with mode: 0644]
priv/repo/migrations/20191026191218_set_not_null_for_moderation_log.exs [new file with mode: 0644]
priv/repo/migrations/20191026191249_set_not_null_for_notifications.exs [new file with mode: 0644]
priv/repo/migrations/20191026191328_set_not_null_for_oauth_authorizations.exs [new file with mode: 0644]
priv/repo/migrations/20191026191401_set_not_null_for_oauth_tokens.exs [new file with mode: 0644]
priv/repo/migrations/20191026191442_set_not_null_for_objects.exs [new file with mode: 0644]
priv/repo/migrations/20191026191524_set_not_null_for_password_reset_tokens.exs [new file with mode: 0644]
priv/repo/migrations/20191026191603_set_not_null_for_push_subscriptions.exs [new file with mode: 0644]
priv/repo/migrations/20191026191635_set_not_null_for_registrations.exs [new file with mode: 0644]
priv/repo/migrations/20191026191711_set_not_null_for_scheduled_activities.exs [new file with mode: 0644]
priv/repo/migrations/20191026191753_set_not_null_for_thread_mutes.exs [new file with mode: 0644]
priv/repo/migrations/20191026191826_set_not_null_for_user_invite_tokens.exs [new file with mode: 0644]
priv/repo/migrations/20191026191910_set_not_null_for_users.exs [new file with mode: 0644]
priv/static/schemas/litepub-0.1.jsonld
rel/files/bin/pleroma_ctl
test/conversation/participation_test.exs
test/daemons/digest_email_daemon_test.exs
test/emails/user_email_test.exs
test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.json [new file with mode: 0644]
test/fixtures/tesla_mock/moonman@shitposter.club.json [new file with mode: 0644]
test/fixtures/tesla_mock/relay@mastdon.example.org.json [new file with mode: 0644]
test/formatter_test.exs
test/marker_test.exs [new file with mode: 0644]
test/moderation_log_test.exs
test/notification_test.exs
test/object/containment_test.exs
test/object/fetcher_test.exs
test/plugs/admin_secret_authentication_plug_test.exs
test/plugs/user_enabled_plug_test.exs
test/plugs/user_is_admin_plug_test.exs
test/safe_jsonb_set_test.exs [new file with mode: 0644]
test/signature_test.exs
test/support/factory.ex
test/support/http_request_mock.ex
test/tasks/count_statuses_test.exs
test/tasks/database_test.exs
test/tasks/user_test.exs
test/user_info_test.exs [deleted file]
test/user_search_test.exs
test/user_test.exs
test/web/activity_pub/activity_pub_controller_test.exs
test/web/activity_pub/activity_pub_test.exs
test/web/activity_pub/mrf/anti_link_spam_policy_test.exs
test/web/activity_pub/publisher_test.exs
test/web/activity_pub/relay_test.exs
test/web/activity_pub/transmogrifier/follow_handling_test.exs
test/web/activity_pub/transmogrifier_test.exs
test/web/activity_pub/utils_test.exs
test/web/activity_pub/views/user_view_test.exs
test/web/admin_api/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/federator_test.exs
test/web/masto_fe_controller_test.exs
test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs
test/web/mastodon_api/controllers/account_controller_test.exs
test/web/mastodon_api/controllers/conversation_controller_test.exs
test/web/mastodon_api/controllers/follow_request_controller_test.exs
test/web/mastodon_api/controllers/instance_controller_test.exs
test/web/mastodon_api/controllers/marker_controller_test.exs [new file with mode: 0644]
test/web/mastodon_api/controllers/search_controller_test.exs
test/web/mastodon_api/controllers/status_controller_test.exs
test/web/mastodon_api/controllers/timeline_controller_test.exs
test/web/mastodon_api/mastodon_api_test.exs
test/web/mastodon_api/views/account_view_test.exs
test/web/mastodon_api/views/marker_view_test.exs [new file with mode: 0644]
test/web/mastodon_api/views/status_view_test.exs
test/web/node_info_test.exs
test/web/oauth/oauth_controller_test.exs
test/web/ostatus/activity_representer_test.exs [deleted file]
test/web/ostatus/feed_representer_test.exs [deleted file]
test/web/ostatus/incoming_documents/delete_handling_test.exs [deleted file]
test/web/ostatus/ostatus_controller_test.exs
test/web/ostatus/ostatus_test.exs [deleted file]
test/web/ostatus/user_representer_test.exs [deleted file]
test/web/pleroma_api/controllers/account_controller_test.exs
test/web/pleroma_api/controllers/emoji_api_controller_test.exs
test/web/pleroma_api/controllers/pleroma_api_controller_test.exs
test/web/push/impl_test.exs
test/web/salmon/salmon_test.exs [deleted file]
test/web/streamer/streamer_test.exs
test/web/twitter_api/password_controller_test.exs
test/web/twitter_api/twitter_api_test.exs
test/web/twitter_api/util_controller_test.exs
test/web/web_finger/web_finger_test.exs
test/web/websub/websub_controller_test.exs [deleted file]
test/web/websub/websub_test.exs [deleted file]

index 36137b38e09b73e7bd47a0880c59680d230af436..04af8c186283fe701ad982ea27d5c8e100c92e0d 100644 (file)
@@ -15,6 +15,7 @@ cache:
 stages:
   - build
   - test
+  - benchmark
   - deploy
   - release
 
@@ -28,6 +29,36 @@ build:
   - mix deps.get
   - mix compile --force
 
+docs-build:
+  stage: build
+  only:
+  - master@pleroma/pleroma
+  - develop@pleroma/pleroma
+  variables:
+    MIX_ENV: dev
+    PLEROMA_BUILD_ENV: prod
+  script:
+    - mix deps.get
+    - mix compile
+    - mix docs
+  artifacts:
+    paths:
+      - priv/static/doc
+
+benchmark:
+  stage: benchmark
+  variables:
+    MIX_ENV: benchmark
+  services:
+  - name: lainsoykaf/postgres-with-rum
+    alias: postgres
+    command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
+  script:
+    - mix deps.get
+    - mix ecto.create
+    - mix ecto.migrate
+    - mix pleroma.load_testing
+
 unit-testing:
   stage: test
   services:
index 4744567d11f51c1211c20a4abeb35a42614da99d..00dd62e24039b1df8ce7903c293fa093f07558f8 100644 (file)
@@ -4,8 +4,39 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ## [Unreleased]
+### Removed
+- **Breaking**: Removed 1.0+ deprecated configurations `Pleroma.Upload, :strip_exif` and `:instance, :dedupe_media`
+- **Breaking**: OStatus protocol support
+
+### Changed
+- **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
+- Replaced [pleroma_job_queue](https://git.pleroma.social/pleroma/pleroma_job_queue) and `Pleroma.Web.Federator.RetryQueue` with [Oban](https://github.com/sorentwo/oban) (see [`docs/config.md`](docs/config.md) on migrating customized worker / retry settings)
+- Introduced [quantum](https://github.com/quantum-elixir/quantum-core) job scheduler
+- Enabled `:instance, extended_nickname_format` in the default config
+- Add `rel="ugc"` to all links in statuses, to prevent SEO spam
+- Extract RSS functionality from OStatus
+- MRF (Simple Policy): Also use `:accept`/`:reject` on the actors rather than only their activities
+<details>
+  <summary>API Changes</summary>
+
+- **Breaking:** Admin API: Return link alongside with token on password reset
+- **Breaking:** `/api/pleroma/admin/users/invite_token` now uses `POST`, changed accepted params and returns full invite in json instead of only token string.
+- Admin API: Return `total` when querying for reports
+- Mastodon API: Return `pleroma.direct_conversation_id` when creating a direct message (`POST /api/v1/statuses`)
+- Admin API: Return link alongside with token on password reset
+- Mastodon API: Add `pleroma.direct_conversation_id` to the status endpoint (`GET /api/v1/statuses/:id`)
+- Mastodon API: `pleroma.thread_muted` to the Status entity
+- Mastodon API: Mark the direct conversation as read for the author when they send a new direct message
+</details>
+
 ### Added
 - Refreshing poll results for remote polls
+- Authentication: Added rate limit for password-authorized actions / login existence checks
+- Mix task to re-count statuses for all users (`mix pleroma.count_statuses`)
+- Support for `X-Forwarded-For` and similar HTTP headers which used by reverse proxies to pass a real user IP address to the backend. Must not be enabled unless your instance is behind at least one reverse proxy (such as Nginx, Apache HTTPD or Varnish Cache).
+<details>
+  <summary>API Changes</summary>
+
 - Job queue stats to the healthcheck page
 - Admin API: Add ability to require password reset
 - Mastodon API: Account entities now include `follow_requests_count` (planned Mastodon 3.x addition)
@@ -14,10 +45,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Mastodon API: Add `upload_limit`, `avatar_upload_limit`, `background_upload_limit`, and `banner_upload_limit` to `/api/v1/instance`
 - Mastodon API: Add `pleroma.unread_conversation_count` to the Account entity
 - OAuth: support for hierarchical permissions / [Mastodon 2.4.3 OAuth permissions](https://docs.joinmastodon.org/api/permissions/)
-- Authentication: Added rate limit for password-authorized actions / login existence checks
 - Metadata Link: Atom syndication Feed
-- Mix task to re-count statuses for all users (`mix pleroma.count_statuses`)
 - Mastodon API: Add `exclude_visibilities` parameter to the timeline and notification endpoints
+- Admin API: `/users/:nickname/toggle_activation` endpoint is now deprecated in favor of: `/users/activate`, `/users/deactivate`, both accept `nicknames` array
+- Admin API: `POST/DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group` are deprecated in favor of: `POST/DELETE /api/pleroma/admin/users/permission_group/:permission_group` (both accept `nicknames` array), `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body).
+- Admin API: Add `GET /api/pleroma/admin/relay` endpoint - lists all followed relays
+- Pleroma API: `POST /api/v1/pleroma/conversations/read` to mark all conversations as read
+- Mastodon API: Add `/api/v1/markers` for managing timeline read markers
 
 ### Changed
 - **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
@@ -30,16 +64,39 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - MRF (Simple Policy): Also use `:accept`/`:reject` on the actors rather than only their activities
 - OStatus: Extract RSS functionality
 - Mastodon API: Add `pleroma.direct_conversation_id` to the status endpoint (`GET /api/v1/statuses/:id`)
+- Mastodon API: Mark the direct conversation as read for the author when they send a new direct message
+- Deprecated `User.Info` embedded schema (fields moved to `User`)
 - Store status data inside Flag activity
+</details>
 
 ### Fixed
+- Report emails now include functional links to profiles of remote user accounts
+<details>
+  <summary>API Changes</summary>
+
 - Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`)
 - Mastodon API: Inability to get some local users by nickname in `/api/v1/accounts/:id_or_nickname`
-- Added `:instance, extended_nickname_format` setting to the default config
-- Report emails now include functional links to profiles of remote user accounts
+</details>
+
+## [1.1.2] - 2019-10-18
+### Fixed
+- `pleroma_ctl` trying to connect to a running instance when generating the config, which of course doesn't exist.
 
-## [1.1.0] - 2019-??-??
-**Breaking:** The stable branch has been changed from `master` to `stable`, `master` now points to `release/1.0`
+## [1.1.1] - 2019-10-18
+### Fixed
+- One of the migrations between 1.0.0 and 1.1.0 wiping user info of the relay user because of unexpected behavior of postgresql's `jsonb_set`, resulting in inability to post in the default configuration. If you were affected, please run the following query in postgres console, the relay user will be recreated automatically:
+```
+delete from users where ap_id = 'https://your.instance.hostname/relay';
+```
+- Bad user search matches
+
+## [1.1.0] - 2019-10-14
+**Breaking:** The stable branch has been changed from `master` to `stable`. If you want to keep using 1.0, the `release/1.0` branch will receive security updates for 6 months after 1.1 release.
+
+**OTP Note:** `pleroma_ctl` in 1.0 defaults to `master` and doesn't support specifying arbitrary branches, making `./pleroma_ctl update` fail. To fix this, fetch a version of `pleroma_ctl` from 1.1 using the command below and proceed with the update normally:
+```
+curl -Lo ./bin/pleroma_ctl 'https://git.pleroma.social/pleroma/pleroma/raw/develop/rel/files/bin/pleroma_ctl'
+```
 ### Security
 - Mastodon API: respect post privacy in `/api/v1/statuses/:id/{favourited,reblogged}_by`
 
@@ -47,16 +104,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - **Breaking:** GNU Social API with Qvitter extensions support
 - Emoji: Remove longfox emojis.
 - Remove `Reply-To` header from report emails for admins.
+- ActivityPub: The `/objects/:uuid/likes` endpoint.
 
 ### Changed
 - **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config
 - **Breaking:** Configuration: `/media/` is now removed when `base_url` is configured, append `/media/` to your `base_url` config to keep the old behaviour if desired
 - **Breaking:** `/api/pleroma/notifications/read` is moved to `/api/v1/pleroma/notifications/read` and now supports `max_id` and responds with Mastodon API entities.
-- **Breaking:** `/api/pleroma/admin/users/invite_token` now uses `POST`, changed accepted params and returns full invite in json instead of only token string.
 - Configuration: added `config/description.exs`, from which `docs/config.md` is generated
 - Configuration: OpenGraph and TwitterCard providers enabled by default
 - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
-- Mastodon API: `pleroma.thread_muted` key in the Status entity
 - Federation: Return 403 errors when trying to request pages from a user's follower/following collections if they have `hide_followers`/`hide_follows` set
 - NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
 - NodeInfo: Return `mailerEnabled` in `metadata`
@@ -65,7 +121,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - AdminAPI: Add "godmode" while fetching user statuses (i.e. admin can see private statuses)
 - Improve digest email template
 – Pagination: (optional) return `total` alongside with `items` when paginating
-- Add `rel="ugc"` to all links in statuses, to prevent SEO spam
+- The `Pleroma.FlakeId` module has been replaced with the `flake_id` library.
 
 ### Fixed
 - Following from Osada
@@ -76,21 +132,28 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Mastodon API: Misskey's endless polls being unable to render
 - Mastodon API: Embedded relationships not being properly rendered in the Account entity of Status entity
 - Mastodon API: Notifications endpoint crashing if one notification failed to render
+- Mastodon API: `exclude_replies` is correctly handled again.
 - Mastodon API: Add `account_id`, `type`, `offset`, and `limit` to search API (`/api/v1/search` and `/api/v2/search`)
 - Mastodon API, streaming: Fix filtering of notifications based on blocks/mutes/thread mutes
-- ActivityPub C2S: follower/following collection pages being inaccessible even when authentifucated if `hide_followers`/ `hide_follows` was set
-- Existing user id not being preserved on insert conflict
+- Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`)
+- Mastodon API: Ensure the `account` field is not empty when rendering Notification entities.
+- Mastodon API: Inability to get some local users by nickname in `/api/v1/accounts/:id_or_nickname`
+- Mastodon API: Blocks are now treated consistently between the Streaming API and the Timeline APIs
 - Rich Media: Parser failing when no TTL can be found by image TTL setters
 - Rich Media: The crawled URL is now spliced into the rich media data.
 - ActivityPub S2S: sharedInbox usage has been mostly aligned with the rules in the AP specification.
-- Pleroma.Upload base_url was not automatically whitelisted by MediaProxy. Now your custom CDN or file hosting will be accessed directly as expected.
-- Report email not being sent to admins when the reporter is a remote user
-- Reverse Proxy limiting `max_body_length` was incorrectly defined and only checked `Content-Length` headers which may not be sufficient in some circumstances
+- ActivityPub C2S: follower/following collection pages being inaccessible even when authentifucated if `hide_followers`/ `hide_follows` was set
 - ActivityPub: Deactivated user deletion
 - ActivityPub: Fix `/users/:nickname/inbox` crashing without an authenticated user
 - MRF: fix ability to follow a relay when AntiFollowbotPolicy was enabled
-- Mastodon API: Blocks are now treated consistently between the Streaming API and the Timeline APIs
-- Mastodon API: `exclude_replies` is correctly handled again.
+- ActivityPub: Correct addressing of Undo.
+- ActivityPub: Correct addressing of profile update activities.
+- ActivityPub: Polls are now refreshed when necessary.
+- Report emails now include functional links to profiles of remote user accounts
+- Existing user id not being preserved on insert conflict
+- Pleroma.Upload base_url was not automatically whitelisted by MediaProxy. Now your custom CDN or file hosting will be accessed directly as expected.
+- Report email not being sent to admins when the reporter is a remote user
+- Reverse Proxy limiting `max_body_length` was incorrectly defined and only checked `Content-Length` headers which may not be sufficient in some circumstances
 
 ### Added
 - Expiring/ephemeral activites. All activities can have expires_at value set, which controls when they should be deleted automatically.
@@ -104,6 +167,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Mastodon API: Support for the [`tagged` filter](https://github.com/tootsuite/mastodon/pull/9755) in [`GET /api/v1/accounts/:id/statuses`](https://docs.joinmastodon.org/api/rest/accounts/#get-api-v1-accounts-id-statuses)
 - Mastodon API, streaming: Add support for passing the token in the `Sec-WebSocket-Protocol` header
 - Mastodon API, extension: Ability to reset avatar, profile banner, and background
+- Mastodon API: Add support for `fields_attributes` API parameter (setting custom fields)
 - Mastodon API: Add support for categories for custom emojis by reusing the group feature. <https://github.com/tootsuite/mastodon/pull/11196>
 - Mastodon API: Add support for muting/unmuting notifications
 - Mastodon API: Add support for the `blocked_by` attribute in the relationship API (`GET /api/v1/accounts/relationships`). <https://github.com/tootsuite/mastodon/pull/10373>
@@ -112,7 +176,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Mastodon API: added `/auth/password` endpoint for password reset with rate limit.
 - Mastodon API: /api/v1/accounts/:id/statuses now supports nicknames or user id
 - Mastodon API: Improve support for the user profile custom fields
-- Mastodon API: follower/following counters are nullified when `hide_follows`/`hide_followers` and `hide_follows_count`/`hide_followers_count` are set
+- Mastodon API: Add support for `fields_attributes` API parameter (setting custom fields)
+- Mastodon API: Added an endpoint to get multiple statuses by IDs (`GET /api/v1/statuses/?ids[]=1&ids[]=2`)
 - Admin API: Return users' tags when querying reports
 - Admin API: Return avatar and display name when querying users
 - Admin API: Allow querying user by ID
@@ -130,11 +195,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Pleroma API: Add `/api/v1/pleroma/accounts/confirmation_resend?email=<email>` for resending account confirmation.
 - Pleroma API: Email change endpoint.
 - Admin API: Added moderation log
-- Support for `X-Forwarded-For` and similar HTTP headers which used by reverse proxies to pass a real user IP address to the backend. Must not be enabled unless your instance is behind at least one reverse proxy (such as Nginx, Apache HTTPD or Varnish Cache).
 - Web response cache (currently, enabled for ActivityPub)
-- Mastodon API: Added an endpoint to get multiple statuses by IDs (`GET /api/v1/statuses/?ids[]=1&ids[]=2`)
-- ActivityPub: Add ActivityPub actor's `discoverable` parameter.
-- Admin API: Added moderation log filters (user/start date/end date/search/pagination)
 - Reverse Proxy: Do not retry failed requests to limit pressure on the peer
 
 ### Changed
index 846442346b7e391a54b3fe309acff774a3b140f3..dd49822e9c1530e9302abf048936880d7011e74e 100644 (file)
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@ While we don’t provide docker files, other people have written very good ones.
 
 ### Dependencies
 
-* Postgresql version 9.6 or newer
+* Postgresql version 9.6 or newer, including the contrib modules
 * Elixir version 1.7 or newer. If your distribution only has an old version available, check [Elixir’s install page](https://elixir-lang.org/install.html) or use a tool like [asdf](https://github.com/asdf-vm/asdf).
 * Build-essential tools
 
@@ -71,7 +71,7 @@ This is useful for running Pleroma inside Tor or I2P.
 
 ## Customization and contribution
 
-The [Pleroma Documentation](https://docs-develop.pleroma.social/readme.html) offers manuals and guides on how to further customize your instance to your liking and how you can contribute to the project.
+The [Pleroma Documentation](https://docs-develop.pleroma.social) offers manuals and guides on how to further customize your instance to your liking and how you can contribute to the project.
 
 ## Troubleshooting
 
diff --git a/benchmarks/load_testing/fetcher.ex b/benchmarks/load_testing/fetcher.ex
new file mode 100644 (file)
index 0000000..e378c51
--- /dev/null
@@ -0,0 +1,229 @@
+defmodule Pleroma.LoadTesting.Fetcher do
+  use Pleroma.LoadTesting.Helper
+
+  def fetch_user(user) do
+    Benchee.run(%{
+      "By id" => fn -> Repo.get_by(User, id: user.id) end,
+      "By ap_id" => fn -> Repo.get_by(User, ap_id: user.ap_id) end,
+      "By email" => fn -> Repo.get_by(User, email: user.email) end,
+      "By nickname" => fn -> Repo.get_by(User, nickname: user.nickname) end
+    })
+  end
+
+  def query_timelines(user) do
+    home_timeline_params = %{
+      "count" => 20,
+      "with_muted" => true,
+      "type" => ["Create", "Announce"],
+      "blocking_user" => user,
+      "muting_user" => user,
+      "user" => user
+    }
+
+    mastodon_public_timeline_params = %{
+      "count" => 20,
+      "local_only" => true,
+      "only_media" => "false",
+      "type" => ["Create", "Announce"],
+      "with_muted" => "true",
+      "blocking_user" => user,
+      "muting_user" => user
+    }
+
+    mastodon_federated_timeline_params = %{
+      "count" => 20,
+      "only_media" => "false",
+      "type" => ["Create", "Announce"],
+      "with_muted" => "true",
+      "blocking_user" => user,
+      "muting_user" => user
+    }
+
+    Benchee.run(%{
+      "User home timeline" => fn ->
+        Pleroma.Web.ActivityPub.ActivityPub.fetch_activities(
+          [user.ap_id | user.following],
+          home_timeline_params
+        )
+      end,
+      "User mastodon public timeline" => fn ->
+        Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities(
+          mastodon_public_timeline_params
+        )
+      end,
+      "User mastodon federated public timeline" => fn ->
+        Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities(
+          mastodon_federated_timeline_params
+        )
+      end
+    })
+
+    home_activities =
+      Pleroma.Web.ActivityPub.ActivityPub.fetch_activities(
+        [user.ap_id | user.following],
+        home_timeline_params
+      )
+
+    public_activities =
+      Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities(mastodon_public_timeline_params)
+
+    public_federated_activities =
+      Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities(
+        mastodon_federated_timeline_params
+      )
+
+    Benchee.run(%{
+      "Rendering home timeline" => fn ->
+        Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
+          activities: home_activities,
+          for: user,
+          as: :activity
+        })
+      end,
+      "Rendering public timeline" => fn ->
+        Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
+          activities: public_activities,
+          for: user,
+          as: :activity
+        })
+      end,
+      "Rendering public federated timeline" => fn ->
+        Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
+          activities: public_federated_activities,
+          for: user,
+          as: :activity
+        })
+      end
+    })
+  end
+
+  def query_notifications(user) do
+    without_muted_params = %{"count" => "20", "with_muted" => "false"}
+    with_muted_params = %{"count" => "20", "with_muted" => "true"}
+
+    Benchee.run(%{
+      "Notifications without muted" => fn ->
+        Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, without_muted_params)
+      end,
+      "Notifications with muted" => fn ->
+        Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, with_muted_params)
+      end
+    })
+
+    without_muted_notifications =
+      Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, without_muted_params)
+
+    with_muted_notifications =
+      Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, with_muted_params)
+
+    Benchee.run(%{
+      "Render notifications without muted" => fn ->
+        Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
+          notifications: without_muted_notifications,
+          for: user
+        })
+      end,
+      "Render notifications with muted" => fn ->
+        Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
+          notifications: with_muted_notifications,
+          for: user
+        })
+      end
+    })
+  end
+
+  def query_dms(user) do
+    params = %{
+      "count" => "20",
+      "with_muted" => "true",
+      "type" => "Create",
+      "blocking_user" => user,
+      "user" => user,
+      visibility: "direct"
+    }
+
+    Benchee.run(%{
+      "Direct messages with muted" => fn ->
+        Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params)
+        |> Pleroma.Pagination.fetch_paginated(params)
+      end,
+      "Direct messages without muted" => fn ->
+        Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params)
+        |> Pleroma.Pagination.fetch_paginated(Map.put(params, "with_muted", false))
+      end
+    })
+
+    dms_with_muted =
+      Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params)
+      |> Pleroma.Pagination.fetch_paginated(params)
+
+    dms_without_muted =
+      Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params)
+      |> Pleroma.Pagination.fetch_paginated(Map.put(params, "with_muted", false))
+
+    Benchee.run(%{
+      "Rendering dms with muted" => fn ->
+        Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
+          activities: dms_with_muted,
+          for: user,
+          as: :activity
+        })
+      end,
+      "Rendering dms without muted" => fn ->
+        Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
+          activities: dms_without_muted,
+          for: user,
+          as: :activity
+        })
+      end
+    })
+  end
+
+  def query_long_thread(user, activity) do
+    Benchee.run(%{
+      "Fetch main post" => fn ->
+        Pleroma.Activity.get_by_id_with_object(activity.id)
+      end,
+      "Fetch context of main post" => fn ->
+        Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_for_context(
+          activity.data["context"],
+          %{
+            "blocking_user" => user,
+            "user" => user,
+            "exclude_id" => activity.id
+          }
+        )
+      end
+    })
+
+    activity = Pleroma.Activity.get_by_id_with_object(activity.id)
+
+    context =
+      Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_for_context(
+        activity.data["context"],
+        %{
+          "blocking_user" => user,
+          "user" => user,
+          "exclude_id" => activity.id
+        }
+      )
+
+    Benchee.run(%{
+      "Render status" => fn ->
+        Pleroma.Web.MastodonAPI.StatusView.render("show.json", %{
+          activity: activity,
+          for: user
+        })
+      end,
+      "Render context" => fn ->
+        Pleroma.Web.MastodonAPI.StatusView.render(
+          "index.json",
+          for: user,
+          activities: context,
+          as: :activity
+        )
+        |> Enum.reverse()
+      end
+    })
+  end
+end
diff --git a/benchmarks/load_testing/generator.ex b/benchmarks/load_testing/generator.ex
new file mode 100644 (file)
index 0000000..5c5a5c1
--- /dev/null
@@ -0,0 +1,352 @@
+defmodule Pleroma.LoadTesting.Generator do
+  use Pleroma.LoadTesting.Helper
+  alias Pleroma.Web.CommonAPI
+
+  def generate_users(opts) do
+    IO.puts("Starting generating #{opts[:users_max]} users...")
+    {time, _} = :timer.tc(fn -> do_generate_users(opts) end)
+
+    IO.puts("Inserting users take #{to_sec(time)} sec.\n")
+  end
+
+  defp do_generate_users(opts) do
+    max = Keyword.get(opts, :users_max)
+
+    Task.async_stream(
+      1..max,
+      &generate_user_data(&1),
+      max_concurrency: 10,
+      timeout: 30_000
+    )
+    |> Enum.to_list()
+  end
+
+  defp generate_user_data(i) do
+    remote = Enum.random([true, false])
+
+    user = %User{
+      name: "Test テスト User #{i}",
+      email: "user#{i}@example.com",
+      nickname: "nick#{i}",
+      password_hash:
+        "$pbkdf2-sha512$160000$bU.OSFI7H/yqWb5DPEqyjw$uKp/2rmXw12QqnRRTqTtuk2DTwZfF8VR4MYW2xMeIlqPR/UX1nT1CEKVUx2CowFMZ5JON8aDvURrZpJjSgqXrg",
+      bio: "Tester Number #{i}",
+      info: %{},
+      local: remote
+    }
+
+    user_urls =
+      if remote do
+        base_url =
+          Enum.random(["https://domain1.com", "https://domain2.com", "https://domain3.com"])
+
+        ap_id = "#{base_url}/users/#{user.nickname}"
+
+        %{
+          ap_id: ap_id,
+          follower_address: ap_id <> "/followers",
+          following_address: ap_id <> "/following",
+          following: [ap_id]
+        }
+      else
+        %{
+          ap_id: User.ap_id(user),
+          follower_address: User.ap_followers(user),
+          following_address: User.ap_following(user),
+          following: [User.ap_id(user)]
+        }
+      end
+
+    user = Map.merge(user, user_urls)
+
+    Repo.insert!(user)
+  end
+
+  def generate_activities(user, users) do
+    do_generate_activities(user, users)
+  end
+
+  defp do_generate_activities(user, users) do
+    IO.puts("Starting generating 20000 common activities...")
+
+    {time, _} =
+      :timer.tc(fn ->
+        Task.async_stream(
+          1..20_000,
+          fn _ ->
+            do_generate_activity([user | users])
+          end,
+          max_concurrency: 10,
+          timeout: 30_000
+        )
+        |> Stream.run()
+      end)
+
+    IO.puts("Inserting common activities take #{to_sec(time)} sec.\n")
+
+    IO.puts("Starting generating 20000 activities with mentions...")
+
+    {time, _} =
+      :timer.tc(fn ->
+        Task.async_stream(
+          1..20_000,
+          fn _ ->
+            do_generate_activity_with_mention(user, users)
+          end,
+          max_concurrency: 10,
+          timeout: 30_000
+        )
+        |> Stream.run()
+      end)
+
+    IO.puts("Inserting activities with menthions take #{to_sec(time)} sec.\n")
+
+    IO.puts("Starting generating 10000 activities with threads...")
+
+    {time, _} =
+      :timer.tc(fn ->
+        Task.async_stream(
+          1..10_000,
+          fn _ ->
+            do_generate_threads([user | users])
+          end,
+          max_concurrency: 10,
+          timeout: 30_000
+        )
+        |> Stream.run()
+      end)
+
+    IO.puts("Inserting activities with threads take #{to_sec(time)} sec.\n")
+  end
+
+  defp do_generate_activity(users) do
+    post = %{
+      "status" => "Some status without mention with random user"
+    }
+
+    CommonAPI.post(Enum.random(users), post)
+  end
+
+  defp do_generate_activity_with_mention(user, users) do
+    mentions_cnt = Enum.random([2, 3, 4, 5])
+    with_user = Enum.random([true, false])
+    users = Enum.shuffle(users)
+    mentions_users = Enum.take(users, mentions_cnt)
+    mentions_users = if with_user, do: [user | mentions_users], else: mentions_users
+
+    mentions_str =
+      Enum.map(mentions_users, fn user -> "@" <> user.nickname end) |> Enum.join(", ")
+
+    post = %{
+      "status" => mentions_str <> "some status with mentions random users"
+    }
+
+    CommonAPI.post(Enum.random(users), post)
+  end
+
+  defp do_generate_threads(users) do
+    thread_length = Enum.random([2, 3, 4, 5])
+    actor = Enum.random(users)
+
+    post = %{
+      "status" => "Start of the thread"
+    }
+
+    {:ok, activity} = CommonAPI.post(actor, post)
+
+    Enum.each(1..thread_length, fn _ ->
+      user = Enum.random(users)
+
+      post = %{
+        "status" => "@#{actor.nickname} reply to thread",
+        "in_reply_to_status_id" => activity.id
+      }
+
+      CommonAPI.post(user, post)
+    end)
+  end
+
+  def generate_remote_activities(user, users) do
+    do_generate_remote_activities(user, users)
+  end
+
+  defp do_generate_remote_activities(user, users) do
+    IO.puts("Starting generating 10000 remote activities...")
+
+    {time, _} =
+      :timer.tc(fn ->
+        Task.async_stream(
+          1..10_000,
+          fn i ->
+            do_generate_remote_activity(i, user, users)
+          end,
+          max_concurrency: 10,
+          timeout: 30_000
+        )
+        |> Stream.run()
+      end)
+
+    IO.puts("Inserting remote activities take #{to_sec(time)} sec.\n")
+  end
+
+  defp do_generate_remote_activity(i, user, users) do
+    actor = Enum.random(users)
+    %{host: host} = URI.parse(actor.ap_id)
+    date = Date.utc_today()
+    datetime = DateTime.utc_now()
+
+    map = %{
+      "actor" => actor.ap_id,
+      "cc" => [actor.follower_address, user.ap_id],
+      "context" => "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation",
+      "id" => actor.ap_id <> "/statuses/#{i}/activity",
+      "object" => %{
+        "actor" => actor.ap_id,
+        "atomUri" => actor.ap_id <> "/statuses/#{i}",
+        "attachment" => [],
+        "attributedTo" => actor.ap_id,
+        "bcc" => [],
+        "bto" => [],
+        "cc" => [actor.follower_address, user.ap_id],
+        "content" =>
+          "<p><span class=\"h-card\"><a href=\"" <>
+            user.ap_id <>
+            "\" class=\"u-url mention\">@<span>" <> user.nickname <> "</span></a></span></p>",
+        "context" => "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation",
+        "conversation" =>
+          "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation",
+        "emoji" => %{},
+        "id" => actor.ap_id <> "/statuses/#{i}",
+        "inReplyTo" => nil,
+        "inReplyToAtomUri" => nil,
+        "published" => datetime,
+        "sensitive" => true,
+        "summary" => "cw",
+        "tag" => [
+          %{
+            "href" => user.ap_id,
+            "name" => "@#{user.nickname}@#{host}",
+            "type" => "Mention"
+          }
+        ],
+        "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+        "type" => "Note",
+        "url" => "http://#{host}/@#{actor.nickname}/#{i}"
+      },
+      "published" => datetime,
+      "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+      "type" => "Create"
+    }
+
+    Pleroma.Web.ActivityPub.ActivityPub.insert(map, false)
+  end
+
+  def generate_dms(user, users, opts) do
+    IO.puts("Starting generating #{opts[:dms_max]} DMs")
+    {time, _} = :timer.tc(fn -> do_generate_dms(user, users, opts) end)
+    IO.puts("Inserting dms take #{to_sec(time)} sec.\n")
+  end
+
+  defp do_generate_dms(user, users, opts) do
+    Task.async_stream(
+      1..opts[:dms_max],
+      fn _ ->
+        do_generate_dm(user, users)
+      end,
+      max_concurrency: 10,
+      timeout: 30_000
+    )
+    |> Stream.run()
+  end
+
+  defp do_generate_dm(user, users) do
+    post = %{
+      "status" => "@#{user.nickname} some direct message",
+      "visibility" => "direct"
+    }
+
+    CommonAPI.post(Enum.random(users), post)
+  end
+
+  def generate_long_thread(user, users, opts) do
+    IO.puts("Starting generating long thread with #{opts[:thread_length]} replies")
+    {time, activity} = :timer.tc(fn -> do_generate_long_thread(user, users, opts) end)
+    IO.puts("Inserting long thread replies take #{to_sec(time)} sec.\n")
+    {:ok, activity}
+  end
+
+  defp do_generate_long_thread(user, users, opts) do
+    {:ok, %{id: id} = activity} = CommonAPI.post(user, %{"status" => "Start of long thread"})
+
+    Task.async_stream(
+      1..opts[:thread_length],
+      fn _ -> do_generate_thread(users, id) end,
+      max_concurrency: 10,
+      timeout: 30_000
+    )
+    |> Stream.run()
+
+    activity
+  end
+
+  defp do_generate_thread(users, activity_id) do
+    CommonAPI.post(Enum.random(users), %{
+      "status" => "reply to main post",
+      "in_reply_to_status_id" => activity_id
+    })
+  end
+
+  def generate_non_visible_message(user, users) do
+    IO.puts("Starting generating 1000 non visible posts")
+
+    {time, _} =
+      :timer.tc(fn ->
+        do_generate_non_visible_posts(user, users)
+      end)
+
+    IO.puts("Inserting non visible posts take #{to_sec(time)} sec.\n")
+  end
+
+  defp do_generate_non_visible_posts(user, users) do
+    [not_friend | users] = users
+
+    make_friends(user, users)
+
+    Task.async_stream(1..1000, fn _ -> do_generate_non_visible_post(not_friend, users) end,
+      max_concurrency: 10,
+      timeout: 30_000
+    )
+    |> Stream.run()
+  end
+
+  defp make_friends(_user, []), do: nil
+
+  defp make_friends(user, [friend | users]) do
+    {:ok, _} = User.follow(user, friend)
+    {:ok, _} = User.follow(friend, user)
+    make_friends(user, users)
+  end
+
+  defp do_generate_non_visible_post(not_friend, users) do
+    post = %{
+      "status" => "some non visible post",
+      "visibility" => "private"
+    }
+
+    {:ok, activity} = CommonAPI.post(not_friend, post)
+
+    thread_length = Enum.random([2, 3, 4, 5])
+
+    Enum.each(1..thread_length, fn _ ->
+      user = Enum.random(users)
+
+      post = %{
+        "status" => "@#{not_friend.nickname} reply to non visible post",
+        "in_reply_to_status_id" => activity.id,
+        "visibility" => "private"
+      }
+
+      CommonAPI.post(user, post)
+    end)
+  end
+end
diff --git a/benchmarks/load_testing/helper.ex b/benchmarks/load_testing/helper.ex
new file mode 100644 (file)
index 0000000..47b25c6
--- /dev/null
@@ -0,0 +1,11 @@
+defmodule Pleroma.LoadTesting.Helper do
+  defmacro __using__(_) do
+    quote do
+      import Ecto.Query
+      alias Pleroma.Repo
+      alias Pleroma.User
+
+      defp to_sec(microseconds), do: microseconds / 1_000_000
+    end
+  end
+end
diff --git a/benchmarks/mix/tasks/pleroma/load_testing.ex b/benchmarks/mix/tasks/pleroma/load_testing.ex
new file mode 100644 (file)
index 0000000..4fa3eec
--- /dev/null
@@ -0,0 +1,134 @@
+defmodule Mix.Tasks.Pleroma.LoadTesting do
+  use Mix.Task
+  use Pleroma.LoadTesting.Helper
+  import Mix.Pleroma
+  import Pleroma.LoadTesting.Generator
+  import Pleroma.LoadTesting.Fetcher
+
+  @shortdoc "Factory for generation data"
+  @moduledoc """
+  Generates data like:
+  - local/remote users
+  - local/remote activities with notifications
+  - direct messages
+  - long thread
+  - non visible posts
+
+  ## Generate data
+      MIX_ENV=benchmark mix pleroma.load_testing --users 20000 --dms 20000 --thread_length 2000
+      MIX_ENV=benchmark mix pleroma.load_testing -u 20000 -d 20000 -t 2000
+
+  Options:
+  - `--users NUMBER` - number of users to generate. Defaults to: 20000. Alias: `-u`
+  - `--dms NUMBER` - number of direct messages to generate. Defaults to: 20000. Alias `-d`
+  - `--thread_length` - number of messages in thread. Defaults to: 2000. ALias `-t`
+  """
+
+  @aliases [u: :users, d: :dms, t: :thread_length]
+  @switches [
+    users: :integer,
+    dms: :integer,
+    thread_length: :integer
+  ]
+  @users_default 20_000
+  @dms_default 1_000
+  @thread_length_default 2_000
+
+  def run(args) do
+    start_pleroma()
+    Pleroma.Config.put([:instance, :skip_thread_containment], true)
+    {opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases)
+
+    users_max = Keyword.get(opts, :users, @users_default)
+    dms_max = Keyword.get(opts, :dms, @dms_default)
+    thread_length = Keyword.get(opts, :thread_length, @thread_length_default)
+
+    clean_tables()
+
+    opts =
+      Keyword.put(opts, :users_max, users_max)
+      |> Keyword.put(:dms_max, dms_max)
+      |> Keyword.put(:thread_length, thread_length)
+
+    generate_users(opts)
+
+    # main user for queries
+    IO.puts("Fetching local main user...")
+
+    {time, user} =
+      :timer.tc(fn ->
+        Repo.one(
+          from(u in User, where: u.local == true, order_by: fragment("RANDOM()"), limit: 1)
+        )
+      end)
+
+    IO.puts("Fetching main user take #{to_sec(time)} sec.\n")
+
+    IO.puts("Fetching local users...")
+
+    {time, users} =
+      :timer.tc(fn ->
+        Repo.all(
+          from(u in User,
+            where: u.id != ^user.id,
+            where: u.local == true,
+            order_by: fragment("RANDOM()"),
+            limit: 10
+          )
+        )
+      end)
+
+    IO.puts("Fetching local users take #{to_sec(time)} sec.\n")
+
+    IO.puts("Fetching remote users...")
+
+    {time, remote_users} =
+      :timer.tc(fn ->
+        Repo.all(
+          from(u in User,
+            where: u.id != ^user.id,
+            where: u.local == false,
+            order_by: fragment("RANDOM()"),
+            limit: 10
+          )
+        )
+      end)
+
+    IO.puts("Fetching remote users take #{to_sec(time)} sec.\n")
+
+    generate_activities(user, users)
+
+    generate_remote_activities(user, remote_users)
+
+    generate_dms(user, users, opts)
+
+    {:ok, activity} = generate_long_thread(user, users, opts)
+
+    generate_non_visible_message(user, users)
+
+    IO.puts("Users in DB: #{Repo.aggregate(from(u in User), :count, :id)}")
+
+    IO.puts("Activities in DB: #{Repo.aggregate(from(a in Pleroma.Activity), :count, :id)}")
+
+    IO.puts("Objects in DB: #{Repo.aggregate(from(o in Pleroma.Object), :count, :id)}")
+
+    IO.puts(
+      "Notifications in DB: #{Repo.aggregate(from(n in Pleroma.Notification), :count, :id)}"
+    )
+
+    fetch_user(user)
+    query_timelines(user)
+    query_notifications(user)
+    query_dms(user)
+    query_long_thread(user, activity)
+    Pleroma.Config.put([:instance, :skip_thread_containment], false)
+    query_timelines(user)
+  end
+
+  defp clean_tables do
+    IO.puts("Deleting old data...\n")
+    Ecto.Adapters.SQL.query!(Repo, "TRUNCATE users CASCADE;")
+    Ecto.Adapters.SQL.query!(Repo, "TRUNCATE activities CASCADE;")
+    Ecto.Adapters.SQL.query!(Repo, "TRUNCATE objects CASCADE;")
+  end
+end
diff --git a/config/benchmark.exs b/config/benchmark.exs
new file mode 100644 (file)
index 0000000..dd99cf5
--- /dev/null
@@ -0,0 +1,84 @@
+use Mix.Config
+
+# We don't run a server during test. If one is required,
+# you can enable the server option below.
+config :pleroma, Pleroma.Web.Endpoint,
+  http: [port: 4001],
+  url: [port: 4001],
+  server: true
+
+# Disable captha for tests
+config :pleroma, Pleroma.Captcha,
+  # It should not be enabled for automatic tests
+  enabled: false,
+  # A fake captcha service for tests
+  method: Pleroma.Captcha.Mock
+
+# Print only warnings and errors during test
+config :logger, level: :warn
+
+config :pleroma, :auth, oauth_consumer_strategies: []
+
+config :pleroma, Pleroma.Upload, filters: [], link_name: false
+
+config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
+
+config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Test, enabled: true
+
+config :pleroma, :instance,
+  email: "admin@example.com",
+  notify_email: "noreply@example.com",
+  skip_thread_containment: false,
+  federating: false,
+  external_user_synchronization: false
+
+config :pleroma, :activitypub, sign_object_fetches: false
+
+# Configure your database
+config :pleroma, Pleroma.Repo,
+  adapter: Ecto.Adapters.Postgres,
+  username: "postgres",
+  password: "postgres",
+  database: "pleroma_test",
+  hostname: System.get_env("DB_HOST") || "localhost",
+  pool_size: 10
+
+# Reduce hash rounds for testing
+config :pbkdf2_elixir, rounds: 1
+
+config :tesla, adapter: Tesla.Mock
+
+config :pleroma, :rich_media,
+  enabled: false,
+  ignore_hosts: [],
+  ignore_tld: ["local", "localdomain", "lan"]
+
+config :web_push_encryption, :vapid_details,
+  subject: "mailto:administrator@example.com",
+  public_key:
+    "BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4",
+  private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA"
+
+config :web_push_encryption, :http_client, Pleroma.Web.WebPushHttpClientMock
+
+config :pleroma_job_queue, disabled: true
+
+config :pleroma, Pleroma.ScheduledActivity,
+  daily_user_limit: 2,
+  total_user_limit: 3,
+  enabled: false
+
+config :pleroma, :rate_limit,
+  search: [{1000, 30}, {1000, 30}],
+  app_account_creation: {10_000, 5},
+  password_reset: {1000, 30}
+
+config :pleroma, :http_security, report_uri: "https://endpoint.com"
+
+config :pleroma, :http, send_user_agent: false
+
+rum_enabled = System.get_env("RUM_ENABLED") == "true"
+config :pleroma, :database, rum_enabled: rum_enabled
+IO.puts("RUM enabled: #{rum_enabled}")
+
+config :pleroma, Pleroma.ReverseProxy.Client, Pleroma.ReverseProxy.ClientMock
index f4d92102f70743f6f8d367eb977d11d0a073c7f8..a69d41d17075259964197f4c3e99101b723ca986 100644 (file)
@@ -59,10 +59,6 @@ scheduled_jobs =
     _ -> []
   end
 
-scheduled_jobs =
-  scheduled_jobs ++
-    [{"0 */6 * * * *", {Pleroma.Web.Websub, :refresh_subscriptions, []}}]
-
 config :pleroma, Pleroma.Scheduler,
   global: true,
   overlap: true,
@@ -243,9 +239,7 @@ config :pleroma, :instance,
   federation_incoming_replies_max_depth: 100,
   federation_reachability_timeout_days: 7,
   federation_publisher_modules: [
-    Pleroma.Web.ActivityPub.Publisher,
-    Pleroma.Web.Websub,
-    Pleroma.Web.Salmon
+    Pleroma.Web.ActivityPub.Publisher
   ],
   allow_relay: true,
   rewrite_policy: Pleroma.Web.ActivityPub.MRF.NoOpPolicy,
@@ -328,6 +322,16 @@ config :pleroma, :assets,
   ],
   default_mascot: :pleroma_fox_tan
 
+config :pleroma, :manifest,
+  icons: [
+    %{
+      src: "/static/logo.png",
+      type: "image/png"
+    }
+  ],
+  theme_color: "#282c37",
+  background_color: "#191b22"
+
 config :pleroma, :activitypub,
   unfollow_blocked: true,
   outgoing_blocks: true,
index b007cf69ca9a83097525d5da56012d8dd11f084d..70e963399c8de149ccba0ef97f294b02d4b45ac3 100644 (file)
@@ -581,9 +581,7 @@ config :pleroma, :config_description, [
         type: [:list, :module],
         description: "List of modules for federation publishing",
         suggestions: [
-          Pleroma.Web.ActivityPub.Publisher,
-          Pleroma.Web.Websub,
-          Pleroma.Web.Salmo
+          Pleroma.Web.ActivityPub.Publisher
         ]
       },
       %{
@@ -1100,6 +1098,45 @@ config :pleroma, :config_description, [
       }
     ]
   },
+  %{
+    group: :pleroma,
+    key: :manifest,
+    type: :group,
+    description:
+      "This section describe PWA manifest instance-specific values. Currently this option relate only for MastoFE",
+    children: [
+      %{
+        key: :icons,
+        type: {:list, :map},
+        description: "Describe the icons of the app",
+        suggestion: [
+          %{
+            src: "/static/logo.png"
+          },
+          %{
+            src: "/static/icon.png",
+            type: "image/png"
+          },
+          %{
+            src: "/static/icon.ico",
+            sizes: "72x72 96x96 128x128 256x256"
+          }
+        ]
+      },
+      %{
+        key: :theme_color,
+        type: :string,
+        description: "Describe the theme color of the app",
+        suggestions: ["#282c37", "mediumpurple"]
+      },
+      %{
+        key: :background_color,
+        type: :string,
+        description: "Describe the background color of the app",
+        suggestions: ["#191b22", "aliceblue"]
+      }
+    ]
+  },
   %{
     group: :pleroma,
     key: :mrf_simple,
index 98c5ceccd54a5501dfefcd8a845d148aedca390f..36c49367384be2eb63b9089ffdfbfd6ae1f1b278 100644 (file)
@@ -1,6 +1,6 @@
 import Config
 
-config :pleroma, :instance, static_dir: "/var/lib/pleroma/static"
+config :pleroma, :instance, static: "/var/lib/pleroma/static"
 config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
 
 config_path = System.get_env("PLEROMA_CONFIG_PATH") || "/etc/pleroma/config.exs"
index ee9e68cb1456c79c3231fa9a259e9dddbdfbbacb..e64ae6429496266b4dd4043a22f3922bc563f80c 100644 (file)
@@ -47,7 +47,7 @@ Authentication is required and the user must be an admin.
 }
 ```
 
-## `/api/pleroma/admin/users`
+## DEPRECATED `DELETE /api/pleroma/admin/users`
 
 ### Remove a user
 
@@ -56,6 +56,15 @@ Authentication is required and the user must be an admin.
   - `nickname`
 - Response: User’s nickname
 
+## `DELETE /api/pleroma/admin/users`
+
+### Remove a user
+
+- Method `DELETE`
+- Params:
+  - `nicknames`
+- Response: Array of user nicknames
+
 ### Create a user
 
 - Method: `POST`
@@ -154,28 +163,86 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
 }
 ```
 
-### Add user in permission group
+## DEPRECATED `POST /api/pleroma/admin/users/:nickname/permission_group/:permission_group`
+
+### Add user to permission group
 
-- Method: `POST`
 - Params: none
 - Response:
   - On failure: `{"error": "…"}`
-  - On success: JSON of the `user.info`
+  - On success: JSON of the user
+
+## `POST /api/pleroma/admin/users/permission_group/:permission_group`
+
+### Add users to permission group
+
+- Params:
+  - `nicknames`: nicknames array
+- Response:
+  - On failure: `{"error": "…"}`
+  - On success: JSON of the user
+
+## DEPRECATED `DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group`
 
 ### Remove user from permission group
 
-- Method: `DELETE`
 - Params: none
 - Response:
   - On failure: `{"error": "…"}`
-  - On success: JSON of the `user.info`
+  - On success: JSON of the user
 - Note: An admin cannot revoke their own admin status.
 
-## `/api/pleroma/admin/users/:nickname/activation_status`
+## `DELETE /api/pleroma/admin/users/permission_group/:permission_group`
+
+### Remove users from permission group
+
+- Params:
+  - `nicknames`: nicknames array
+- Response:
+  - On failure: `{"error": "…"}`
+  - On success: JSON of the user
+- Note: An admin cannot revoke their own admin status.
+
+## `PATCH /api/pleroma/admin/users/activate`
+
+### Activate user
+
+- Params:
+  - `nicknames`: nicknames array
+- Response:
+
+```json
+{
+  users: [
+    {
+      // user object
+    }
+  ]
+}
+```
+
+## `PATCH /api/pleroma/admin/users/deactivate`
+
+### Deactivate user
+
+- Params:
+  - `nicknames`: nicknames array
+- Response:
+
+```json
+{
+  users: [
+    {
+      // user object
+    }
+  ]
+}
+```
+
+## DEPRECATED `PATCH /api/pleroma/admin/users/:nickname/activation_status`
 
 ### Active or deactivate a user
 
-- Method: `PUT`
 - Params:
   - `nickname`
   - `status` BOOLEAN field, false value means deactivation.
@@ -222,6 +289,14 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
 - Response:
   - On success: URL of the unfollowed relay
 
+## `GET /api/pleroma/admin/relay`
+
+### List Relays
+
+- Params: none
+- Response:
+  - On success: JSON array of relays
+
 ## `/api/pleroma/admin/users/invite_token`
 
 ### Create an account registration invite token
index 0517bbdd70ce27febf6af5a33d21041437edc7e6..6c326dc9bc19efea8ef4e063faa6058d47353459 100644 (file)
@@ -367,6 +367,13 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa
     * `recipients`: A list of ids of users that should receive posts to this conversation. This will replace the current list of recipients, so submit the full list. The owner of owner of the conversation will always be part of the set of recipients, though.
 * Response: JSON, statuses (200 - healthy, 503 unhealthy)
 
+## `GET /api/v1/pleroma/conversations/read`
+### Marks all user's conversations as read.
+* Method `POST`
+* Authentication: required
+* Params: None
+* Response: JSON, returns a list of Mastodon Conversation entities that were marked as read (200 - healthy, 503 unhealthy).
+
 ## `GET /api/pleroma/emoji/packs`
 ### Lists the custom emoji packs on the server
 * Method `GET`
index 8d85276edd85e8dfe17b092ac1cefedee4677836..3427ae419429f80a4a870dbaf87d6e5a81e9d322 100644 (file)
@@ -247,6 +247,35 @@ relates to mascots on the mastodon frontend
 * `default_mascot`: An element from `mascots` - This will be used as the default mascot
   on MastoFE (default: `:pleroma_fox_tan`)
 
+## :manifest
+
+This section describe PWA manifest instance-specific values. Currently this option relate only for MastoFE.
+
+* `icons`: Describe the icons of the app, this a list of maps describing icons in the same way as the
+  [spec](https://www.w3.org/TR/appmanifest/#imageresource-and-its-members) describes it.
+
+  Example:
+
+  ```elixir
+  config :pleroma, :manifest,
+    icons: [
+      %{
+        src: "/static/logo.png"
+      },
+      %{
+        src: "/static/icon.png",
+        type: "image/png"
+      },
+      %{
+        src: "/static/icon.ico",
+        sizes: "72x72 96x96 128x128 256x256"
+      }
+    ]
+  ```
+
+* `theme_color`: Describe the theme color of the app. (Example: `"#282c37"`, `"rebeccapurple"`)
+* `background_color`: Describe the background color of the app. (Example: `"#191b22"`, `"aliceblue"`)
+
 ## :mrf_simple
 * `media_removal`: List of instances to remove medias from
 * `media_nsfw`: List of instances to put medias as NSFW(sensitive) from
index cfd9eeada46ced0fd7a3449e59c45fe89f8aac82..8a827ca80fb0c14a1e92bbc8945b36fb57c880a5 100644 (file)
@@ -28,7 +28,7 @@ defmodule Mix.Tasks.Pleroma.Database do
     Logger.info("Removing embedded objects")
 
     Repo.query!(
-      "update activities set data = jsonb_set(data, '{object}'::text[], data->'object'->'id') where data->'object'->>'id' is not null;",
+      "update activities set data = safe_jsonb_set(data, '{object}'::text[], data->'object'->'id') where data->'object'->>'id' is not null;",
       [],
       timeout: :infinity
     )
@@ -126,7 +126,7 @@ defmodule Mix.Tasks.Pleroma.Database do
         set: [
           data:
             fragment(
-              "jsonb_set(?, '{likes}', '[]'::jsonb, true)",
+              "safe_jsonb_set(?, '{likes}', '[]'::jsonb, true)",
               object.data
             )
         ]
index 6ef0a635d083ad2788b0cc3afdb69f2bc6f6c9af..35669af274301228b571534223a31d5e6c2a745f 100644 (file)
@@ -111,19 +111,21 @@ defmodule Mix.Tasks.Pleroma.Emoji do
             file_list: files_to_unzip
           )
 
-        IO.puts(IO.ANSI.format(["Writing emoji.txt for ", :bright, pack_name]))
-
-        emoji_txt_str =
-          Enum.map(
-            files,
-            fn {shortcode, path} ->
-              emojo_path = Path.join("/emoji/#{pack_name}", path)
-              "#{shortcode}, #{emojo_path}"
-            end
-          )
-          |> Enum.join("\n")
-
-        File.write!(Path.join(pack_path, "emoji.txt"), emoji_txt_str)
+        IO.puts(IO.ANSI.format(["Writing pack.json for ", :bright, pack_name]))
+
+        pack_json = %{
+          pack: %{
+            "license" => pack["license"],
+            "homepage" => pack["homepage"],
+            "description" => pack["description"],
+            "fallback-src" => pack["src"],
+            "fallback-src-sha256" => pack["src_sha256"],
+            "share-files" => true
+          },
+          files: files
+        }
+
+        File.write!(Path.join(pack_path, "pack.json"), Jason.encode!(pack_json, pretty: true))
       else
         IO.puts(IO.ANSI.format([:bright, :red, "No pack named \"#{pack_name}\" found"]))
       end
index d7a7b599fee4f3a97a52edceb043f80b282fe5e6..7ef5f9678534a8669cb434910d5b56a78b7866f3 100644 (file)
@@ -5,7 +5,6 @@
 defmodule Mix.Tasks.Pleroma.Relay do
   use Mix.Task
   import Mix.Pleroma
-  alias Pleroma.User
   alias Pleroma.Web.ActivityPub.Relay
 
   @shortdoc "Manages remote relays"
@@ -36,13 +35,10 @@ defmodule Mix.Tasks.Pleroma.Relay do
   def run(["list"]) do
     start_pleroma()
 
-    with %User{following: following} = _user <- Relay.get_actor() do
-      following
-      |> Enum.map(fn entry -> URI.parse(entry).host end)
-      |> Enum.uniq()
-      |> Enum.each(&shell_info(&1))
+    with {:ok, list} <- Relay.list() do
+      list |> Enum.each(&shell_info(&1))
     else
-      e -> shell_error("Error while fetching relay subscription list: #{inspect(e)}")
+      {:error, e} -> shell_error("Error while fetching relay subscription list: #{inspect(e)}")
     end
   end
 end
index 134b5bcccb4a66058071b44ed2f6e2f36ba1831f..d7bdc2310d7c8eec6674634c3b81d51a679389aa 100644 (file)
@@ -5,6 +5,7 @@
 defmodule Mix.Tasks.Pleroma.User do
   use Mix.Task
   import Mix.Pleroma
+  alias Ecto.Changeset
   alias Pleroma.User
   alias Pleroma.UserInviteToken
   alias Pleroma.Web.OAuth
@@ -109,10 +110,10 @@ defmodule Mix.Tasks.Pleroma.User do
     start_pleroma()
 
     with %User{} = user <- User.get_cached_by_nickname(nickname) do
-      {:ok, user} = User.deactivate(user, !user.info.deactivated)
+      {:ok, user} = User.deactivate(user, !user.deactivated)
 
       shell_info(
-        "Activation status of #{nickname}: #{if(user.info.deactivated, do: "de", else: "")}activated"
+        "Activation status of #{nickname}: #{if(user.deactivated, do: "de", else: "")}activated"
       )
     else
       _ ->
@@ -340,7 +341,7 @@ defmodule Mix.Tasks.Pleroma.User do
     with %User{} = user <- User.get_cached_by_nickname(nickname) do
       {:ok, user} = User.toggle_confirmation(user)
 
-      message = if user.info.confirmation_pending, do: "needs", else: "doesn't need"
+      message = if user.confirmation_pending, do: "needs", else: "doesn't need"
 
       shell_info("#{nickname} #{message} confirmation.")
     else
@@ -364,23 +365,32 @@ defmodule Mix.Tasks.Pleroma.User do
   end
 
   defp set_moderator(user, value) do
-    {:ok, user} = User.update_info(user, &User.Info.admin_api_update(&1, %{is_moderator: value}))
+    {:ok, user} =
+      user
+      |> Changeset.change(%{is_moderator: value})
+      |> User.update_and_set_cache()
 
-    shell_info("Moderator status of #{user.nickname}: #{user.info.is_moderator}")
+    shell_info("Moderator status of #{user.nickname}: #{user.is_moderator}")
     user
   end
 
   defp set_admin(user, value) do
-    {:ok, user} = User.update_info(user, &User.Info.admin_api_update(&1, %{is_admin: value}))
+    {:ok, user} =
+      user
+      |> Changeset.change(%{is_admin: value})
+      |> User.update_and_set_cache()
 
-    shell_info("Admin status of #{user.nickname}: #{user.info.is_admin}")
+    shell_info("Admin status of #{user.nickname}: #{user.is_admin}")
     user
   end
 
   defp set_locked(user, value) do
-    {:ok, user} = User.update_info(user, &User.Info.user_upgrade(&1, %{locked: value}))
+    {:ok, user} =
+      user
+      |> Changeset.change(%{locked: value})
+      |> User.update_and_set_cache()
 
-    shell_info("Locked status of #{user.nickname}: #{user.info.locked}")
+    shell_info("Locked status of #{user.nickname}: #{user.locked}")
     user
   end
 end
index 0bf218bc7d2fb6c9bf1b5c3b46bd29a59fc6e9aa..d681eecc80f95920577e6c81263800c6e3d89d53 100644 (file)
@@ -161,11 +161,6 @@ defmodule Pleroma.Application do
         id: :web_push_init,
         start: {Task, :start_link, [&Pleroma.Web.Push.init/0]},
         restart: :temporary
-      },
-      %{
-        id: :federator_init,
-        start: {Task, :start_link, [&Pleroma.Web.Federator.init/0]},
-        restart: :temporary
       }
     ]
   end
@@ -177,11 +172,6 @@ defmodule Pleroma.Application do
         start: {Task, :start_link, [&Pleroma.Web.Push.init/0]},
         restart: :temporary
       },
-      %{
-        id: :federator_init,
-        start: {Task, :start_link, [&Pleroma.Web.Federator.init/0]},
-        restart: :temporary
-      },
       %{
         id: :internal_fetch_init,
         start: {Task, :start_link, [&Pleroma.Web.ActivityPub.InternalFetchActor.init/0]},
index 098016af28c7b839bd6f019d4d61ea6e59ab3c7a..ade3a526a57e52b1f69006eb383dd438c03f2df6 100644 (file)
@@ -67,7 +67,13 @@ defmodule Pleroma.Conversation do
 
       participations =
         Enum.map(users, fn user ->
-          User.increment_unread_conversation_count(conversation, user)
+          invisible_conversation = Enum.any?(users, &User.blocks?(user, &1))
+
+          unless invisible_conversation do
+            User.increment_unread_conversation_count(conversation, user)
+          end
+
+          opts = Keyword.put(opts, :invisible_conversation, invisible_conversation)
 
           {:ok, participation} =
             Participation.create_for_user_and_conversation(user, conversation, opts)
index ab81f32173fd1a69694eed8aa39c1be0013843bc..176b82a2092e46cd48745c719a5f4c6fd4dd206d 100644 (file)
@@ -32,11 +32,20 @@ defmodule Pleroma.Conversation.Participation do
 
   def create_for_user_and_conversation(user, conversation, opts \\ []) do
     read = !!opts[:read]
+    invisible_conversation = !!opts[:invisible_conversation]
+
+    update_on_conflict =
+      if(invisible_conversation, do: [], else: [read: read])
+      |> Keyword.put(:updated_at, NaiveDateTime.utc_now())
 
     %__MODULE__{}
-    |> creation_cng(%{user_id: user.id, conversation_id: conversation.id, read: read})
+    |> creation_cng(%{
+      user_id: user.id,
+      conversation_id: conversation.id,
+      read: invisible_conversation || read
+    })
     |> Repo.insert(
-      on_conflict: [set: [read: read, updated_at: NaiveDateTime.utc_now()]],
+      on_conflict: [set: update_on_conflict],
       returning: true,
       conflict_target: [:user_id, :conversation_id]
     )
@@ -48,6 +57,12 @@ defmodule Pleroma.Conversation.Participation do
     |> validate_required([:read])
   end
 
+  def mark_as_read(%User{} = user, %Conversation{} = conversation) do
+    with %__MODULE__{} = participation <- for_user_and_conversation(user, conversation) do
+      mark_as_read(participation)
+    end
+  end
+
   def mark_as_read(participation) do
     participation
     |> read_cng(%{read: true})
@@ -63,6 +78,38 @@ defmodule Pleroma.Conversation.Participation do
     end
   end
 
+  def mark_all_as_read(%User{local: true} = user, %User{} = target_user) do
+    target_conversation_ids =
+      __MODULE__
+      |> where([p], p.user_id == ^target_user.id)
+      |> select([p], p.conversation_id)
+      |> Repo.all()
+
+    __MODULE__
+    |> where([p], p.user_id == ^user.id)
+    |> where([p], p.conversation_id in ^target_conversation_ids)
+    |> update([p], set: [read: true])
+    |> Repo.update_all([])
+
+    {:ok, user} = User.set_unread_conversation_count(user)
+    {:ok, user, []}
+  end
+
+  def mark_all_as_read(%User{} = user, %User{}), do: {:ok, user, []}
+
+  def mark_all_as_read(%User{} = user) do
+    {_, participations} =
+      __MODULE__
+      |> where([p], p.user_id == ^user.id)
+      |> where([p], not p.read)
+      |> update([p], set: [read: true])
+      |> select([p], p)
+      |> Repo.update_all([])
+
+    {:ok, user} = User.set_unread_conversation_count(user)
+    {:ok, user, participations}
+  end
+
   def mark_as_unread(participation) do
     participation
     |> read_cng(%{read: false})
index 462ad2c55eaa5f3b0e9a7d5715bf1ef5f42d7cd2..b4c8eaad9470ce9f421981a24d6c763f7d7c0ed7 100644 (file)
@@ -17,7 +17,7 @@ defmodule Pleroma.Daemons.DigestEmailDaemon do
     now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
 
     from(u in inactive_users_query,
-      where: fragment(~s(? #> '{"email_notifications","digest"}' @> 'true'), u.info),
+      where: fragment(~s(? ->'digest' @> 'true'), u.email_notifications),
       where: u.last_digest_emailed_at < datetime_add(^now, ^negative_interval, "day"),
       select: u
     )
index 40b67ff5633e7bdbff7b98de2223ee867a937e0e..a10f88f936cc23bc55d7c757b93ac6ec0f33e3b9 100644 (file)
@@ -72,7 +72,7 @@ defmodule Pleroma.Emails.UserEmail do
         Endpoint,
         :confirm_email,
         user.id,
-        to_string(user.info.confirmation_token)
+        to_string(user.confirmation_token)
       )
 
     html_body = """
index 931b9af2b4cbbf6735cf25be43739e7533221b26..19b9af46cc3bb48613c3e5e52b1d9f32f591c041 100644 (file)
@@ -127,7 +127,7 @@ defmodule Pleroma.Formatter do
     end
   end
 
-  defp get_ap_id(%User{info: %{source_data: %{"url" => url}}}) when is_binary(url), do: url
+  defp get_ap_id(%User{source_data: %{"url" => url}}) when is_binary(url), do: url
   defp get_ap_id(%User{ap_id: ap_id}), do: ap_id
 
   defp get_nickname_text(nickname, %{mentions_format: :full}), do: User.full_nickname(nickname)
diff --git a/lib/pleroma/marker.ex b/lib/pleroma/marker.ex
new file mode 100644 (file)
index 0000000..7f87c86
--- /dev/null
@@ -0,0 +1,74 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Marker do
+  use Ecto.Schema
+
+  import Ecto.Changeset
+  import Ecto.Query
+
+  alias Ecto.Multi
+  alias Pleroma.Repo
+  alias Pleroma.User
+
+  @timelines ["notifications"]
+
+  schema "markers" do
+    field(:last_read_id, :string, default: "")
+    field(:timeline, :string, default: "")
+    field(:lock_version, :integer, default: 0)
+
+    belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
+    timestamps()
+  end
+
+  def get_markers(user, timelines \\ []) do
+    Repo.all(get_query(user, timelines))
+  end
+
+  def upsert(%User{} = user, attrs) do
+    attrs
+    |> Map.take(@timelines)
+    |> Enum.reduce(Multi.new(), fn {timeline, timeline_attrs}, multi ->
+      marker =
+        user
+        |> get_marker(timeline)
+        |> changeset(timeline_attrs)
+
+      Multi.insert(multi, timeline, marker,
+        returning: true,
+        on_conflict: {:replace, [:last_read_id]},
+        conflict_target: [:user_id, :timeline]
+      )
+    end)
+    |> Repo.transaction()
+  end
+
+  defp get_marker(user, timeline) do
+    case Repo.find_resource(get_query(user, timeline)) do
+      {:ok, marker} -> %__MODULE__{marker | user: user}
+      _ -> %__MODULE__{timeline: timeline, user_id: user.id}
+    end
+  end
+
+  @doc false
+  defp changeset(marker, attrs) do
+    marker
+    |> cast(attrs, [:last_read_id])
+    |> validate_required([:user_id, :timeline, :last_read_id])
+    |> validate_inclusion(:timeline, @timelines)
+  end
+
+  defp by_timeline(query, timeline) do
+    from(m in query, where: m.timeline in ^List.wrap(timeline))
+  end
+
+  defp by_user_id(query, id), do: from(m in query, where: m.user_id == ^id)
+
+  defp get_query(user, timelines) do
+    __MODULE__
+    |> by_user_id(user.id)
+    |> by_timeline(timelines)
+  end
+end
index 352cad4335a40f9c1beea6ac5c2b258191527b72..e8884e6e81c01bf15a3e0247fb4320831647af1c 100644 (file)
@@ -86,18 +86,18 @@ defmodule Pleroma.ModerationLog do
     parsed_datetime
   end
 
-  @spec insert_log(%{actor: User, subject: User, action: String.t(), permission: String.t()}) ::
+  @spec insert_log(%{actor: User, subject: [User], action: String.t(), permission: String.t()}) ::
           {:ok, ModerationLog} | {:error, any}
   def insert_log(%{
         actor: %User{} = actor,
-        subject: %User{} = subject,
+        subject: subjects,
         action: action,
         permission: permission
       }) do
     %ModerationLog{
       data: %{
         "actor" => user_to_map(actor),
-        "subject" => user_to_map(subject),
+        "subject" => user_to_map(subjects),
         "action" => action,
         "permission" => permission,
         "message" => ""
@@ -303,13 +303,16 @@ defmodule Pleroma.ModerationLog do
   end
 
   @spec insert_log_entry_with_message(ModerationLog) :: {:ok, ModerationLog} | {:error, any}
-
   defp insert_log_entry_with_message(entry) do
     entry.data["message"]
     |> put_in(get_log_entry_message(entry))
     |> Repo.insert()
   end
 
+  defp user_to_map(users) when is_list(users) do
+    users |> Enum.map(&user_to_map/1)
+  end
+
   defp user_to_map(%User{} = user) do
     user
     |> Map.from_struct()
@@ -349,10 +352,10 @@ defmodule Pleroma.ModerationLog do
         data: %{
           "actor" => %{"nickname" => actor_nickname},
           "action" => "delete",
-          "subject" => %{"nickname" => subject_nickname, "type" => "user"}
+          "subject" => subjects
         }
       }) do
-    "@#{actor_nickname} deleted user @#{subject_nickname}"
+    "@#{actor_nickname} deleted users: #{users_to_nicknames_string(subjects)}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
@@ -363,12 +366,7 @@ defmodule Pleroma.ModerationLog do
           "subjects" => subjects
         }
       }) do
-    nicknames =
-      subjects
-      |> Enum.map(&"@#{&1["nickname"]}")
-      |> Enum.join(", ")
-
-    "@#{actor_nickname} created users: #{nicknames}"
+    "@#{actor_nickname} created users: #{users_to_nicknames_string(subjects)}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
@@ -376,10 +374,10 @@ defmodule Pleroma.ModerationLog do
         data: %{
           "actor" => %{"nickname" => actor_nickname},
           "action" => "activate",
-          "subject" => %{"nickname" => subject_nickname, "type" => "user"}
+          "subject" => users
         }
       }) do
-    "@#{actor_nickname} activated user @#{subject_nickname}"
+    "@#{actor_nickname} activated users: #{users_to_nicknames_string(users)}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
@@ -387,10 +385,10 @@ defmodule Pleroma.ModerationLog do
         data: %{
           "actor" => %{"nickname" => actor_nickname},
           "action" => "deactivate",
-          "subject" => %{"nickname" => subject_nickname, "type" => "user"}
+          "subject" => users
         }
       }) do
-    "@#{actor_nickname} deactivated user @#{subject_nickname}"
+    "@#{actor_nickname} deactivated users: #{users_to_nicknames_string(users)}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
@@ -402,14 +400,9 @@ defmodule Pleroma.ModerationLog do
           "action" => "tag"
         }
       }) do
-    nicknames_string =
-      nicknames
-      |> Enum.map(&"@#{&1}")
-      |> Enum.join(", ")
-
     tags_string = tags |> Enum.join(", ")
 
-    "@#{actor_nickname} added tags: #{tags_string} to users: #{nicknames_string}"
+    "@#{actor_nickname} added tags: #{tags_string} to users: #{nicknames_to_string(nicknames)}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
@@ -421,14 +414,9 @@ defmodule Pleroma.ModerationLog do
           "action" => "untag"
         }
       }) do
-    nicknames_string =
-      nicknames
-      |> Enum.map(&"@#{&1}")
-      |> Enum.join(", ")
-
     tags_string = tags |> Enum.join(", ")
 
-    "@#{actor_nickname} removed tags: #{tags_string} from users: #{nicknames_string}"
+    "@#{actor_nickname} removed tags: #{tags_string} from users: #{nicknames_to_string(nicknames)}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
@@ -436,11 +424,11 @@ defmodule Pleroma.ModerationLog do
         data: %{
           "actor" => %{"nickname" => actor_nickname},
           "action" => "grant",
-          "subject" => %{"nickname" => subject_nickname},
+          "subject" => users,
           "permission" => permission
         }
       }) do
-    "@#{actor_nickname} made @#{subject_nickname} #{permission}"
+    "@#{actor_nickname} made #{users_to_nicknames_string(users)} #{permission}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
@@ -448,11 +436,11 @@ defmodule Pleroma.ModerationLog do
         data: %{
           "actor" => %{"nickname" => actor_nickname},
           "action" => "revoke",
-          "subject" => %{"nickname" => subject_nickname},
+          "subject" => users,
           "permission" => permission
         }
       }) do
-    "@#{actor_nickname} revoked #{permission} role from @#{subject_nickname}"
+    "@#{actor_nickname} revoked #{permission} role from #{users_to_nicknames_string(users)}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
@@ -551,4 +539,16 @@ defmodule Pleroma.ModerationLog do
       }) do
     "@#{actor_nickname} deleted status ##{subject_id}"
   end
+
+  defp nicknames_to_string(nicknames) do
+    nicknames
+    |> Enum.map(&"@#{&1}")
+    |> Enum.join(", ")
+  end
+
+  defp users_to_nicknames_string(users) do
+    users
+    |> Enum.map(&"@#{&1["nickname"]}")
+    |> Enum.join(", ")
+  end
 end
index d145f8d5b5a42c8ba3b685196754e45a1eaf682e..b7ecf51e4681fc9a25b8d1cd9d596c928f930bba 100644 (file)
@@ -40,7 +40,7 @@ defmodule Pleroma.Notification do
     |> where(
       [n, a],
       fragment(
-        "? not in (SELECT ap_id FROM users WHERE info->'deactivated' @> 'true')",
+        "? not in (SELECT ap_id FROM users WHERE deactivated = 'true')",
         a.actor
       )
     )
@@ -55,21 +55,26 @@ defmodule Pleroma.Notification do
     )
     |> preload([n, a, o], activity: {a, object: o})
     |> exclude_muted(user, opts)
+    |> exclude_blocked(user)
     |> exclude_visibility(opts)
   end
 
+  defp exclude_blocked(query, user) do
+    query
+    |> where([n, a], a.actor not in ^user.blocks)
+    |> where(
+      [n, a],
+      fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.domain_blocks
+    )
+  end
+
   defp exclude_muted(query, _, %{with_muted: true}) do
     query
   end
 
   defp exclude_muted(query, user, _opts) do
     query
-    |> where([n, a], a.actor not in ^user.info.muted_notifications)
-    |> where([n, a], a.actor not in ^user.info.blocks)
-    |> where(
-      [n, a],
-      fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.info.domain_blocks
-    )
+    |> where([n, a], a.actor not in ^user.muted_notifications)
     |> join(:left, [n, a], tm in Pleroma.ThreadMute,
       on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data)
     )
@@ -309,7 +314,7 @@ defmodule Pleroma.Notification do
   def skip?(
         :followers,
         activity,
-        %{info: %{notification_settings: %{"followers" => false}}} = user
+        %{notification_settings: %{"followers" => false}} = user
       ) do
     actor = activity.data["actor"]
     follower = User.get_cached_by_ap_id(actor)
@@ -319,14 +324,14 @@ defmodule Pleroma.Notification do
   def skip?(
         :non_followers,
         activity,
-        %{info: %{notification_settings: %{"non_followers" => false}}} = user
+        %{notification_settings: %{"non_followers" => false}} = user
       ) do
     actor = activity.data["actor"]
     follower = User.get_cached_by_ap_id(actor)
     !User.following?(follower, user)
   end
 
-  def skip?(:follows, activity, %{info: %{notification_settings: %{"follows" => false}}} = user) do
+  def skip?(:follows, activity, %{notification_settings: %{"follows" => false}} = user) do
     actor = activity.data["actor"]
     followed = User.get_cached_by_ap_id(actor)
     User.following?(user, followed)
@@ -335,7 +340,7 @@ defmodule Pleroma.Notification do
   def skip?(
         :non_follows,
         activity,
-        %{info: %{notification_settings: %{"non_follows" => false}}} = user
+        %{notification_settings: %{"non_follows" => false}} = user
       ) do
     actor = activity.data["actor"]
     followed = User.get_cached_by_ap_id(actor)
index cdfbacb0e783c3610f3b9332696c77b929fa7c05..d9b41d710cebb6296a3b2d4cb41521feb2c27030 100644 (file)
@@ -181,7 +181,7 @@ defmodule Pleroma.Object do
         data:
           fragment(
             """
-            jsonb_set(?, '{repliesCount}',
+            safe_jsonb_set(?, '{repliesCount}',
               (coalesce((?->>'repliesCount')::int, 0) + 1)::varchar::jsonb, true)
             """,
             o.data,
@@ -204,7 +204,7 @@ defmodule Pleroma.Object do
         data:
           fragment(
             """
-            jsonb_set(?, '{repliesCount}',
+            safe_jsonb_set(?, '{repliesCount}',
               (greatest(0, (?->>'repliesCount')::int - 1))::varchar::jsonb, true)
             """,
             o.data,
index f077a9f32436841f1ffb01975c970d3082fd80a7..68535c09e66ab962bcbcb5ce91a53303f48b6d0f 100644 (file)
@@ -32,6 +32,23 @@ defmodule Pleroma.Object.Containment do
     get_actor(%{"actor" => actor})
   end
 
+  # TODO: We explicitly allow 'tag' URIs through, due to references to legacy OStatus
+  # objects being present in the test suite environment.  Once these objects are
+  # removed, please also remove this.
+  if Mix.env() == :test do
+    defp compare_uris(_, %URI{scheme: "tag"}), do: :ok
+  end
+
+  defp compare_uris(%URI{} = id_uri, %URI{} = other_uri) do
+    if id_uri.host == other_uri.host do
+      :ok
+    else
+      :error
+    end
+  end
+
+  defp compare_uris(_, _), do: :error
+
   @doc """
   Checks that an imported AP object's actor matches the domain it came from.
   """
@@ -41,11 +58,7 @@ defmodule Pleroma.Object.Containment do
     id_uri = URI.parse(id)
     actor_uri = URI.parse(get_actor(params))
 
-    if id_uri.host == actor_uri.host do
-      :ok
-    else
-      :error
-    end
+    compare_uris(actor_uri, id_uri)
   end
 
   def contain_origin(id, %{"attributedTo" => actor} = params),
@@ -57,11 +70,7 @@ defmodule Pleroma.Object.Containment do
     id_uri = URI.parse(id)
     other_uri = URI.parse(other_id)
 
-    if id_uri.host == other_uri.host do
-      :ok
-    else
-      :error
-    end
+    compare_uris(id_uri, other_uri)
   end
 
   def contain_child(%{"object" => %{"id" => id, "attributedTo" => _} = object}),
index 5e064fd8735a9ea9dbb48f0e13d872204db1b2b4..441ae8b6557e9601e19ab0fd3c781b3812f35b8c 100644 (file)
@@ -10,7 +10,6 @@ defmodule Pleroma.Object.Fetcher do
   alias Pleroma.Signature
   alias Pleroma.Web.ActivityPub.InternalFetchActor
   alias Pleroma.Web.ActivityPub.Transmogrifier
-  alias Pleroma.Web.OStatus
 
   require Logger
   require Pleroma.Constants
@@ -67,7 +66,8 @@ defmodule Pleroma.Object.Fetcher do
          {:normalize, nil} <- {:normalize, Object.normalize(data, false)},
          params <- prepare_activity_params(data),
          {:containment, :ok} <- {:containment, Containment.contain_origin(id, params)},
-         {:ok, activity} <- Transmogrifier.handle_incoming(params, options),
+         {:transmogrifier, {:ok, activity}} <-
+           {:transmogrifier, Transmogrifier.handle_incoming(params, options)},
          {:object, _data, %Object{} = object} <-
            {:object, data, Object.normalize(activity, false)} do
       {:ok, object}
@@ -75,9 +75,12 @@ defmodule Pleroma.Object.Fetcher do
       {:containment, _} ->
         {:error, "Object containment failed."}
 
-      {:error, {:reject, nil}} ->
+      {:transmogrifier, {:error, {:reject, nil}}} ->
         {:reject, nil}
 
+      {:transmogrifier, _} ->
+        {:error, "Transmogrifier failure."}
+
       {:object, data, nil} ->
         reinject_object(%Object{}, data)
 
@@ -87,15 +90,11 @@ defmodule Pleroma.Object.Fetcher do
       {:fetch_object, %Object{} = object} ->
         {:ok, object}
 
-      _e ->
-        # Only fallback when receiving a fetch/normalization error with ActivityPub
-        Logger.info("Couldn't get object via AP, trying out OStatus fetching...")
+      {:fetch, {:error, error}} ->
+        {:error, error}
 
-        # FIXME: OStatus Object Containment?
-        case OStatus.fetch_activity_from_url(id) do
-          {:ok, [activity | _]} -> {:ok, Object.normalize(activity, false)}
-          e -> e
-        end
+      e ->
+        e
     end
   end
 
@@ -114,7 +113,11 @@ defmodule Pleroma.Object.Fetcher do
     with {:ok, object} <- fetch_object_from_id(id, options) do
       object
     else
-      _e ->
+      {:error, %Tesla.Mock.Error{}} ->
+        nil
+
+      e ->
+        Logger.error("Error while fetching #{id}: #{inspect(e)}")
         nil
     end
   end
@@ -161,7 +164,7 @@ defmodule Pleroma.Object.Fetcher do
 
     Logger.debug("Fetch headers: #{inspect(headers)}")
 
-    with true <- String.starts_with?(id, "http"),
+    with {:scheme, true} <- {:scheme, String.starts_with?(id, "http")},
          {:ok, %{body: body, status: code}} when code in 200..299 <- HTTP.get(id, headers),
          {:ok, data} <- Jason.decode(body),
          :ok <- Containment.contain_origin_from_id(id, data) do
@@ -170,6 +173,12 @@ defmodule Pleroma.Object.Fetcher do
       {:ok, %{status: code}} when code in [404, 410] ->
         {:error, "Object has been deleted"}
 
+      {:scheme, _} ->
+        {:error, "Unsupported URI scheme"}
+
+      {:error, e} ->
+        {:error, e}
+
       e ->
         {:error, e}
     end
index 5baf8a6914defa37c6a60d42bb61eab7ba22d87e..fdadd476e6c06320f9bfa708024764789482ccfd 100644 (file)
@@ -19,7 +19,7 @@ defmodule Pleroma.Plugs.AdminSecretAuthenticationPlug do
   def call(%{params: %{"admin_token" => admin_token}} = conn, _) do
     if secret_token() && admin_token == secret_token() do
       conn
-      |> assign(:user, %User{info: %{is_admin: true}})
+      |> assign(:user, %User{is_admin: true})
     else
       conn
     end
index 86bc4aa3a40db3e54e1125542504d5eac5216258..fd004fcd2fec7730f155945a672c2badd6e9ec79 100644 (file)
@@ -71,7 +71,7 @@ defmodule Pleroma.Plugs.OAuthPlug do
       )
 
     # credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
-    with %Token{user: %{info: %{deactivated: false} = _} = user} = token_record <- Repo.one(query) do
+    with %Token{user: %{deactivated: false} = user} = token_record <- Repo.one(query) do
       {:ok, user, token_record}
     end
   end
index da892c28bda3d0419ab6dadb1148a315752ba11f..fbb4bf11575e41178bf0dcdfe55c05511f682567 100644 (file)
@@ -10,7 +10,7 @@ defmodule Pleroma.Plugs.UserEnabledPlug do
     options
   end
 
-  def call(%{assigns: %{user: %User{info: %{deactivated: true}}}} = conn, _) do
+  def call(%{assigns: %{user: %User{deactivated: true}}} = conn, _) do
     conn
     |> assign(:user, nil)
   end
index 4c4b3d610c15ec69e07cd7d2ea63b46d1bc4df68..ee808f31f2f53988ff7bbfdeba61977171ad48e3 100644 (file)
@@ -11,7 +11,7 @@ defmodule Pleroma.Plugs.UserIsAdminPlug do
     options
   end
 
-  def call(%{assigns: %{user: %User{info: %{is_admin: true}}}} = conn, _) do
+  def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _) do
     conn
   end
 
index df80fbaa41f0d62fd252444b16cef22325e1d9cc..8154a09b754a75b36d72b8e01700f0f2612cec7f 100644 (file)
@@ -68,12 +68,7 @@ defmodule Pleroma.Stats do
 
     domain_count = Enum.count(peers)
 
-    status_query =
-      from(u in User.Query.build(%{local: true}),
-        select: fragment("sum((?->>'note_count')::int)", u.info)
-      )
-
-    status_count = Repo.one(status_query)
+    status_count = Repo.aggregate(User.Query.build(%{local: true}), :sum, :note_count)
 
     user_count = Repo.aggregate(User.Query.build(%{local: true, active: true}), :count, :id)
 
index 9f0adde5b1da66ad07b8b812774784e698f829a0..2e0986197402479bf147332bd0155e33ad0fd347 100644 (file)
@@ -105,7 +105,7 @@ defmodule Pleroma.Upload do
           {Pleroma.Config.get!([:instance, :upload_limit]), "Document"}
       end
 
-    opts = %{
+    %{
       activity_type: Keyword.get(opts, :activity_type, activity_type),
       size_limit: Keyword.get(opts, :size_limit, size_limit),
       uploader: Keyword.get(opts, :uploader, Pleroma.Config.get([__MODULE__, :uploader])),
@@ -118,37 +118,6 @@ defmodule Pleroma.Upload do
           Pleroma.Config.get([__MODULE__, :base_url], Pleroma.Web.base_url())
         )
     }
-
-    # TODO: 1.0+ : remove old config compatibility
-    opts =
-      if Pleroma.Config.get([__MODULE__, :strip_exif]) == true &&
-           !Enum.member?(opts.filters, Pleroma.Upload.Filter.Mogrify) do
-        Logger.warn("""
-        Pleroma: configuration `:instance, :strip_exif` is deprecated, please instead set:
-
-          :pleroma, Pleroma.Upload, [filters: [Pleroma.Upload.Filter.Mogrify]]
-
-          :pleroma, Pleroma.Upload.Filter.Mogrify, args: ["strip", "auto-orient"]
-        """)
-
-        Pleroma.Config.put([Pleroma.Upload.Filter.Mogrify], args: ["strip", "auto-orient"])
-        Map.put(opts, :filters, opts.filters ++ [Pleroma.Upload.Filter.Mogrify])
-      else
-        opts
-      end
-
-    if Pleroma.Config.get([:instance, :dedupe_media]) == true &&
-         !Enum.member?(opts.filters, Pleroma.Upload.Filter.Dedupe) do
-      Logger.warn("""
-      Pleroma: configuration `:instance, :dedupe_media` is deprecated, please instead set:
-
-      :pleroma, Pleroma.Upload, [filters: [Pleroma.Upload.Filter.Dedupe]]
-      """)
-
-      Map.put(opts, :filters, opts.filters ++ [Pleroma.Upload.Filter.Dedupe])
-    else
-      opts
-    end
   end
 
   defp prepare_upload(%Plug.Upload{} = file, opts) do
index 2cfb13a8c0164b2b4f1989fae7bdc6a666202c0e..5d3f5572192966b7fff3d0c99a678e25b3058c2d 100644 (file)
@@ -26,9 +26,7 @@ defmodule Pleroma.User do
   alias Pleroma.Web.CommonAPI
   alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
   alias Pleroma.Web.OAuth
-  alias Pleroma.Web.OStatus
   alias Pleroma.Web.RelMe
-  alias Pleroma.Web.Websub
   alias Pleroma.Workers.BackgroundWorker
 
   require Logger
@@ -63,15 +61,70 @@ defmodule Pleroma.User do
     field(:tags, {:array, :string}, default: [])
     field(:last_refreshed_at, :naive_datetime_usec)
     field(:last_digest_emailed_at, :naive_datetime)
+
+    field(:banner, :map, default: %{})
+    field(:background, :map, default: %{})
+    field(:source_data, :map, default: %{})
+    field(:note_count, :integer, default: 0)
+    field(:follower_count, :integer, default: 0)
+    # Should be filled in only for remote users
+    field(:following_count, :integer, default: nil)
+    field(:locked, :boolean, default: false)
+    field(:confirmation_pending, :boolean, default: false)
+    field(:password_reset_pending, :boolean, default: false)
+    field(:confirmation_token, :string, default: nil)
+    field(:default_scope, :string, default: "public")
+    field(:blocks, {:array, :string}, default: [])
+    field(:domain_blocks, {:array, :string}, default: [])
+    field(:mutes, {:array, :string}, default: [])
+    field(:muted_reblogs, {:array, :string}, default: [])
+    field(:muted_notifications, {:array, :string}, default: [])
+    field(:subscribers, {:array, :string}, default: [])
+    field(:deactivated, :boolean, default: false)
+    field(:no_rich_text, :boolean, default: false)
+    field(:ap_enabled, :boolean, default: false)
+    field(:is_moderator, :boolean, default: false)
+    field(:is_admin, :boolean, default: false)
+    field(:show_role, :boolean, default: true)
+    field(:settings, :map, default: nil)
+    field(:magic_key, :string, default: nil)
+    field(:uri, :string, default: nil)
+    field(:hide_followers_count, :boolean, default: false)
+    field(:hide_follows_count, :boolean, default: false)
+    field(:hide_followers, :boolean, default: false)
+    field(:hide_follows, :boolean, default: false)
+    field(:hide_favorites, :boolean, default: true)
+    field(:unread_conversation_count, :integer, default: 0)
+    field(:pinned_activities, {:array, :string}, default: [])
+    field(:email_notifications, :map, default: %{"digest" => false})
+    field(:mascot, :map, default: nil)
+    field(:emoji, {:array, :map}, default: [])
+    field(:pleroma_settings_store, :map, default: %{})
+    field(:fields, {:array, :map}, default: [])
+    field(:raw_fields, {:array, :map}, default: [])
+    field(:discoverable, :boolean, default: false)
+    field(:invisible, :boolean, default: false)
+    field(:skip_thread_containment, :boolean, default: false)
+
+    field(:notification_settings, :map,
+      default: %{
+        "followers" => true,
+        "follows" => true,
+        "non_follows" => true,
+        "non_followers" => true
+      }
+    )
+
     has_many(:notifications, Notification)
     has_many(:registrations, Registration)
     has_many(:deliveries, Delivery)
-    embeds_one(:info, User.Info)
+
+    field(:info, :map, default: %{})
 
     timestamps()
   end
 
-  def auth_active?(%User{info: %User.Info{confirmation_pending: true}}),
+  def auth_active?(%User{confirmation_pending: true}),
     do: !Pleroma.Config.get([:instance, :account_activation_required])
 
   def auth_active?(%User{}), do: true
@@ -86,10 +139,13 @@ defmodule Pleroma.User do
 
   def visible_for?(_, _), do: false
 
-  def superuser?(%User{local: true, info: %User.Info{is_admin: true}}), do: true
-  def superuser?(%User{local: true, info: %User.Info{is_moderator: true}}), do: true
+  def superuser?(%User{local: true, is_admin: true}), do: true
+  def superuser?(%User{local: true, is_moderator: true}), do: true
   def superuser?(_), do: false
 
+  def invisible?(%User{invisible: true}), do: true
+  def invisible?(_), do: false
+
   def avatar_url(user, options \\ []) do
     case user.avatar do
       %{"url" => [%{"href" => href} | _]} -> href
@@ -98,13 +154,13 @@ defmodule Pleroma.User do
   end
 
   def banner_url(user, options \\ []) do
-    case user.info.banner do
+    case user.banner do
       %{"url" => [%{"href" => href} | _]} -> href
       _ -> !options[:no_default] && "#{Web.base_url()}/images/banner.png"
     end
   end
 
-  def profile_url(%User{info: %{source_data: %{"url" => url}}}), do: url
+  def profile_url(%User{source_data: %{"url" => url}}), do: url
   def profile_url(%User{ap_id: ap_id}), do: ap_id
   def profile_url(_), do: nil
 
@@ -119,15 +175,15 @@ defmodule Pleroma.User do
 
   def user_info(%User{} = user, args \\ %{}) do
     following_count =
-      Map.get(args, :following_count, user.info.following_count || following_count(user))
+      Map.get(args, :following_count, user.following_count || following_count(user))
 
-    follower_count = Map.get(args, :follower_count, user.info.follower_count)
+    follower_count = Map.get(args, :follower_count, user.follower_count)
 
     %{
-      note_count: user.info.note_count,
-      locked: user.info.locked,
-      confirmation_pending: user.info.confirmation_pending,
-      default_scope: user.info.default_scope
+      note_count: user.note_count,
+      locked: user.locked,
+      confirmation_pending: user.confirmation_pending,
+      default_scope: user.default_scope
     }
     |> Map.put(:following_count, following_count)
     |> Map.put(:follower_count, follower_count)
@@ -157,9 +213,7 @@ defmodule Pleroma.User do
 
   @spec restrict_deactivated(Ecto.Query.t()) :: Ecto.Query.t()
   def restrict_deactivated(query) do
-    from(u in query,
-      where: not fragment("? \\? 'deactivated' AND ?->'deactivated' @> 'true'", u.info, u.info)
-    )
+    from(u in query, where: u.deactivated != ^true)
   end
 
   def following_count(%User{following: []}), do: 0
@@ -170,6 +224,14 @@ defmodule Pleroma.User do
     |> Repo.aggregate(:count, :id)
   end
 
+  defp truncate_fields_param(params) do
+    if Map.has_key?(params, :fields) do
+      Map.put(params, :fields, Enum.map(params[:fields], &truncate_field/1))
+    else
+      params
+    end
+  end
+
   defp truncate_if_exists(params, key, max_length) do
     if Map.has_key?(params, key) and is_binary(params[key]) do
       {value, _chopped} = String.split_at(params[key], max_length)
@@ -188,18 +250,43 @@ defmodule Pleroma.User do
       |> Map.put(:info, params[:info] || %{})
       |> truncate_if_exists(:name, name_limit)
       |> truncate_if_exists(:bio, bio_limit)
+      |> truncate_fields_param()
 
     changeset =
       %User{local: false}
-      |> cast(params, [:bio, :name, :ap_id, :nickname, :avatar])
+      |> cast(
+        params,
+        [
+          :bio,
+          :name,
+          :ap_id,
+          :nickname,
+          :avatar,
+          :ap_enabled,
+          :source_data,
+          :banner,
+          :locked,
+          :magic_key,
+          :uri,
+          :hide_followers,
+          :hide_follows,
+          :hide_followers_count,
+          :hide_follows_count,
+          :follower_count,
+          :fields,
+          :following_count,
+          :discoverable,
+          :invisible
+        ]
+      )
       |> validate_required([:name, :ap_id])
       |> unique_constraint(:nickname)
       |> validate_format(:nickname, @email_regex)
       |> validate_length(:bio, max: bio_limit)
       |> validate_length(:name, max: name_limit)
-      |> change_info(&User.Info.remote_user_creation(&1, params[:info]))
+      |> validate_fields(true)
 
-    case params[:info][:source_data] do
+    case params[:source_data] do
       %{"followers" => followers, "following" => following} ->
         changeset
         |> put_change(:follower_address, followers)
@@ -216,11 +303,36 @@ defmodule Pleroma.User do
     name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
 
     struct
-    |> cast(params, [:bio, :name, :avatar, :following])
+    |> cast(
+      params,
+      [
+        :bio,
+        :name,
+        :avatar,
+        :following,
+        :locked,
+        :no_rich_text,
+        :default_scope,
+        :banner,
+        :hide_follows,
+        :hide_followers,
+        :hide_followers_count,
+        :hide_follows_count,
+        :hide_favorites,
+        :background,
+        :show_role,
+        :skip_thread_containment,
+        :fields,
+        :raw_fields,
+        :pleroma_settings_store,
+        :discoverable
+      ]
+    )
     |> unique_constraint(:nickname)
     |> validate_format(:nickname, local_nickname_regex())
     |> validate_length(:bio, max: bio_limit)
     |> validate_length(:name, min: 1, max: name_limit)
+    |> validate_fields(false)
   end
 
   def upgrade_changeset(struct, params \\ %{}, remote? \\ false) do
@@ -229,20 +341,38 @@ defmodule Pleroma.User do
 
     params = Map.put(params, :last_refreshed_at, NaiveDateTime.utc_now())
 
+    params = if remote?, do: truncate_fields_param(params), else: params
+
     struct
-    |> cast(params, [
-      :bio,
-      :name,
-      :follower_address,
-      :following_address,
-      :avatar,
-      :last_refreshed_at
-    ])
+    |> cast(
+      params,
+      [
+        :bio,
+        :name,
+        :follower_address,
+        :following_address,
+        :avatar,
+        :last_refreshed_at,
+        :ap_enabled,
+        :source_data,
+        :banner,
+        :locked,
+        :magic_key,
+        :follower_count,
+        :following_count,
+        :hide_follows,
+        :fields,
+        :hide_followers,
+        :discoverable,
+        :hide_followers_count,
+        :hide_follows_count
+      ]
+    )
     |> unique_constraint(:nickname)
     |> validate_format(:nickname, local_nickname_regex())
     |> validate_length(:bio, max: bio_limit)
     |> validate_length(:name, max: name_limit)
-    |> change_info(&User.Info.user_upgrade(&1, params[:info], remote?))
+    |> validate_fields(remote?)
   end
 
   def password_update_changeset(struct, params) do
@@ -250,8 +380,8 @@ defmodule Pleroma.User do
     |> cast(params, [:password, :password_confirmation])
     |> validate_required([:password, :password_confirmation])
     |> validate_confirmation(:password)
-    |> put_password_hash
-    |> put_embed(:info, User.Info.set_password_reset_pending(struct.info, false))
+    |> put_password_hash()
+    |> put_change(:password_reset_pending, false)
   end
 
   @spec reset_password(User.t(), map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
@@ -268,19 +398,19 @@ defmodule Pleroma.User do
     end
   end
 
+  def update_password_reset_pending(user, value) do
+    user
+    |> change()
+    |> put_change(:password_reset_pending, value)
+    |> update_and_set_cache()
+  end
+
   def force_password_reset_async(user) do
     BackgroundWorker.enqueue("force_password_reset", %{"user_id" => user.id})
   end
 
   @spec force_password_reset(User.t()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
-  def force_password_reset(user) do
-    info_cng = User.Info.set_password_reset_pending(user.info, true)
-
-    user
-    |> change()
-    |> put_embed(:info, info_cng)
-    |> update_and_set_cache()
-  end
+  def force_password_reset(user), do: update_password_reset_pending(user, true)
 
   def register_changeset(struct, params \\ %{}, opts \\ []) do
     bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
@@ -294,6 +424,7 @@ defmodule Pleroma.User do
       end
 
     struct
+    |> confirmation_changeset(need_confirmation: need_confirmation?)
     |> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation])
     |> validate_required([:name, :nickname, :password, :password_confirmation])
     |> validate_confirmation(:password)
@@ -304,7 +435,6 @@ defmodule Pleroma.User do
     |> validate_format(:email, @email_regex)
     |> validate_length(:bio, max: bio_limit)
     |> validate_length(:name, min: 1, max: name_limit)
-    |> change_info(&User.Info.confirmation_changeset(&1, need_confirmation: need_confirmation?))
     |> maybe_validate_required_email(opts[:external])
     |> put_password_hash
     |> put_ap_id()
@@ -355,7 +485,7 @@ defmodule Pleroma.User do
   end
 
   def try_send_confirmation_email(%User{} = user) do
-    if user.info.confirmation_pending &&
+    if user.confirmation_pending &&
          Pleroma.Config.get([:instance, :account_activation_required]) do
       user
       |> Pleroma.Emails.UserEmail.account_confirmation_email()
@@ -378,7 +508,7 @@ defmodule Pleroma.User do
   def needs_update?(_), do: true
 
   @spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()}
-  def maybe_direct_follow(%User{} = follower, %User{local: true, info: %{locked: true}}) do
+  def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true}) do
     {:ok, follower}
   end
 
@@ -425,22 +555,18 @@ defmodule Pleroma.User do
     set_cache(follower)
   end
 
-  def follow(%User{} = follower, %User{info: info} = followed) do
+  def follow(%User{} = follower, %User{} = followed) do
     deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
     ap_followers = followed.follower_address
 
     cond do
-      info.deactivated ->
-        {:error, "Could not follow user: You are deactivated."}
+      followed.deactivated ->
+        {:error, "Could not follow user: #{followed.nickname} is deactivated."}
 
       deny_follow_blocked and blocks?(followed, follower) ->
         {:error, "Could not follow user: #{followed.nickname} blocked you."}
 
       true ->
-        if !followed.local && follower.local && !ap_enabled?(followed) do
-          Websub.subscribe(follower, followed)
-        end
-
         q =
           from(u in User,
             where: u.id == ^follower.id,
@@ -489,7 +615,7 @@ defmodule Pleroma.User do
   end
 
   def locked?(%User{} = user) do
-    user.info.locked || false
+    user.locked || false
   end
 
   def get_by_id(id) do
@@ -532,6 +658,12 @@ defmodule Pleroma.User do
     {:ok, user}
   end
 
+  def update_and_set_cache(struct, params) do
+    struct
+    |> update_changeset(params)
+    |> update_and_set_cache()
+  end
+
   def update_and_set_cache(changeset) do
     with {:ok, user} <- Repo.update(changeset, stale_error_field: :id) do
       set_cache(user)
@@ -614,12 +746,7 @@ defmodule Pleroma.User do
     Cachex.fetch!(:user_cache, key, fn -> user_info(user) end)
   end
 
-  def fetch_by_nickname(nickname) do
-    case ActivityPub.make_user_from_nickname(nickname) do
-      {:ok, user} -> {:ok, user}
-      _ -> OStatus.make_user(nickname)
-    end
-  end
+  def fetch_by_nickname(nickname), do: ActivityPub.make_user_from_nickname(nickname)
 
   def get_or_fetch_by_nickname(nickname) do
     with %User{} = user <- get_by_nickname(nickname) do
@@ -721,16 +848,7 @@ defmodule Pleroma.User do
   def increase_note_count(%User{} = user) do
     User
     |> where(id: ^user.id)
-    |> update([u],
-      set: [
-        info:
-          fragment(
-            "jsonb_set(?, '{note_count}', ((?->>'note_count')::int + 1)::varchar::jsonb, true)",
-            u.info,
-            u.info
-          )
-      ]
-    )
+    |> update([u], inc: [note_count: 1])
     |> select([u], u)
     |> Repo.update_all([])
     |> case do
@@ -744,12 +862,7 @@ defmodule Pleroma.User do
     |> where(id: ^user.id)
     |> update([u],
       set: [
-        info:
-          fragment(
-            "jsonb_set(?, '{note_count}', (greatest(0, (?->>'note_count')::int - 1))::varchar::jsonb, true)",
-            u.info,
-            u.info
-          )
+        note_count: fragment("greatest(0, note_count - 1)")
       ]
     )
     |> select([u], u)
@@ -760,28 +873,18 @@ defmodule Pleroma.User do
     end
   end
 
-  def update_note_count(%User{} = user) do
+  def update_note_count(%User{} = user, note_count \\ nil) do
     note_count =
-      from(
-        a in Object,
-        where: fragment("?->>'actor' = ? and ?->>'type' = 'Note'", a.data, ^user.ap_id, a.data),
-        select: count(a.id)
-      )
-      |> Repo.one()
-
-    update_info(user, &User.Info.set_note_count(&1, note_count))
-  end
-
-  def update_mascot(user, url) do
-    info_changeset =
-      User.Info.mascot_update(
-        user.info,
-        url
-      )
+      note_count ||
+        from(
+          a in Object,
+          where: fragment("?->>'actor' = ? and ?->>'type' = 'Note'", a.data, ^user.ap_id, a.data),
+          select: count(a.id)
+        )
+        |> Repo.one()
 
     user
-    |> change()
-    |> put_embed(:info, info_changeset)
+    |> cast(%{note_count: note_count}, [:note_count])
     |> update_and_set_cache()
   end
 
@@ -799,10 +902,24 @@ defmodule Pleroma.User do
 
   def fetch_follow_information(user) do
     with {:ok, info} <- ActivityPub.fetch_follow_information_for_user(user) do
-      update_info(user, &User.Info.follow_information_update(&1, info))
+      user
+      |> follow_information_changeset(info)
+      |> update_and_set_cache()
     end
   end
 
+  defp follow_information_changeset(user, params) do
+    user
+    |> cast(params, [
+      :hide_followers,
+      :hide_follows,
+      :follower_count,
+      :following_count,
+      :hide_followers_count,
+      :hide_follows_count
+    ])
+  end
+
   def update_follower_count(%User{} = user) do
     if user.local or !Pleroma.Config.get([:instance, :external_user_synchronization]) do
       follower_count_query =
@@ -813,14 +930,7 @@ defmodule Pleroma.User do
       |> where(id: ^user.id)
       |> join(:inner, [u], s in subquery(follower_count_query))
       |> update([u, s],
-        set: [
-          info:
-            fragment(
-              "jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)",
-              u.info,
-              s.count
-            )
-        ]
+        set: [follower_count: s.count]
       )
       |> select([u], u)
       |> Repo.update_all([])
@@ -850,14 +960,7 @@ defmodule Pleroma.User do
     User
     |> join(:inner, [u], p in subquery(unread_query))
     |> update([u, p],
-      set: [
-        info:
-          fragment(
-            "jsonb_set(?, '{unread_conversation_count}', ?::varchar::jsonb, true)",
-            u.info,
-            p.count
-          )
-      ]
+      set: [unread_conversation_count: p.count]
     )
     |> where([u], u.id == ^user.id)
     |> select([u], u)
@@ -868,7 +971,7 @@ defmodule Pleroma.User do
     end
   end
 
-  def set_unread_conversation_count(_), do: :noop
+  def set_unread_conversation_count(user), do: {:ok, user}
 
   def increment_unread_conversation_count(conversation, %User{local: true} = user) do
     unread_query =
@@ -878,14 +981,7 @@ defmodule Pleroma.User do
     User
     |> join(:inner, [u], p in subquery(unread_query))
     |> update([u, p],
-      set: [
-        info:
-          fragment(
-            "jsonb_set(?, '{unread_conversation_count}', (coalesce((?->>'unread_conversation_count')::int, 0) + 1)::varchar::jsonb, true)",
-            u.info,
-            u.info
-          )
-      ]
+      inc: [unread_conversation_count: 1]
     )
     |> where([u], u.id == ^user.id)
     |> where([u, p], p.count == 0)
@@ -897,7 +993,7 @@ defmodule Pleroma.User do
     end
   end
 
-  def increment_unread_conversation_count(_, _), do: :noop
+  def increment_unread_conversation_count(_, user), do: {:ok, user}
 
   def remove_duplicated_following(%User{following: following} = user) do
     uniq_following = Enum.uniq(following)
@@ -928,11 +1024,11 @@ defmodule Pleroma.User do
 
   @spec mute(User.t(), User.t(), boolean()) :: {:ok, User.t()} | {:error, String.t()}
   def mute(muter, %User{ap_id: ap_id}, notifications? \\ true) do
-    update_info(muter, &User.Info.add_to_mutes(&1, ap_id, notifications?))
+    add_to_mutes(muter, ap_id, notifications?)
   end
 
   def unmute(muter, %{ap_id: ap_id}) do
-    update_info(muter, &User.Info.remove_from_mutes(&1, ap_id))
+    remove_from_mutes(muter, ap_id)
   end
 
   def subscribe(subscriber, %{ap_id: ap_id}) do
@@ -942,14 +1038,14 @@ defmodule Pleroma.User do
       if blocks?(subscribed, subscriber) and deny_follow_blocked do
         {:error, "Could not subscribe: #{subscribed.nickname} is blocking you"}
       else
-        update_info(subscribed, &User.Info.add_to_subscribers(&1, subscriber.ap_id))
+        User.add_to_subscribers(subscribed, subscriber.ap_id)
       end
     end
   end
 
   def unsubscribe(unsubscriber, %{ap_id: ap_id}) do
     with %User{} = user <- get_cached_by_ap_id(ap_id) do
-      update_info(user, &User.Info.remove_from_subscribers(&1, unsubscriber.ap_id))
+      User.remove_from_subscribers(user, unsubscriber.ap_id)
     end
   end
 
@@ -981,8 +1077,8 @@ defmodule Pleroma.User do
     if following?(blocked, blocker), do: unfollow(blocked, blocker)
 
     {:ok, blocker} = update_follower_count(blocker)
-
-    update_info(blocker, &User.Info.add_to_block(&1, ap_id))
+    {:ok, blocker, _} = Participation.mark_all_as_read(blocker, blocked)
+    add_to_block(blocker, ap_id)
   end
 
   # helper to handle the block given only an actor's AP id
@@ -991,17 +1087,17 @@ defmodule Pleroma.User do
   end
 
   def unblock(blocker, %{ap_id: ap_id}) do
-    update_info(blocker, &User.Info.remove_from_block(&1, ap_id))
+    remove_from_block(blocker, ap_id)
   end
 
   def mutes?(nil, _), do: false
-  def mutes?(user, %{ap_id: ap_id}), do: Enum.member?(user.info.mutes, ap_id)
+  def mutes?(user, %{ap_id: ap_id}), do: Enum.member?(user.mutes, ap_id)
 
   @spec muted_notifications?(User.t() | nil, User.t() | map()) :: boolean()
   def muted_notifications?(nil, _), do: false
 
   def muted_notifications?(user, %{ap_id: ap_id}),
-    do: Enum.member?(user.info.muted_notifications, ap_id)
+    do: Enum.member?(user.muted_notifications, ap_id)
 
   def blocks?(%User{} = user, %User{} = target) do
     blocks_ap_id?(user, target) || blocks_domain?(user, target)
@@ -1010,13 +1106,13 @@ defmodule Pleroma.User do
   def blocks?(nil, _), do: false
 
   def blocks_ap_id?(%User{} = user, %User{} = target) do
-    Enum.member?(user.info.blocks, target.ap_id)
+    Enum.member?(user.blocks, target.ap_id)
   end
 
   def blocks_ap_id?(_, _), do: false
 
   def blocks_domain?(%User{} = user, %User{} = target) do
-    domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.info.domain_blocks)
+    domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.domain_blocks)
     %{host: host} = URI.parse(target.ap_id)
     Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, host)
   end
@@ -1025,42 +1121,42 @@ defmodule Pleroma.User do
 
   def subscribed_to?(user, %{ap_id: ap_id}) do
     with %User{} = target <- get_cached_by_ap_id(ap_id) do
-      Enum.member?(target.info.subscribers, user.ap_id)
+      Enum.member?(target.subscribers, user.ap_id)
     end
   end
 
   @spec muted_users(User.t()) :: [User.t()]
   def muted_users(user) do
-    User.Query.build(%{ap_id: user.info.mutes, deactivated: false})
+    User.Query.build(%{ap_id: user.mutes, deactivated: false})
     |> Repo.all()
   end
 
   @spec blocked_users(User.t()) :: [User.t()]
   def blocked_users(user) do
-    User.Query.build(%{ap_id: user.info.blocks, deactivated: false})
+    User.Query.build(%{ap_id: user.blocks, deactivated: false})
     |> Repo.all()
   end
 
   @spec subscribers(User.t()) :: [User.t()]
   def subscribers(user) do
-    User.Query.build(%{ap_id: user.info.subscribers, deactivated: false})
+    User.Query.build(%{ap_id: user.subscribers, deactivated: false})
     |> Repo.all()
   end
 
-  def block_domain(user, domain) do
-    update_info(user, &User.Info.add_to_domain_block(&1, domain))
+  def deactivate_async(user, status \\ true) do
+    BackgroundWorker.enqueue("deactivate_user", %{"user_id" => user.id, "status" => status})
   end
 
-  def unblock_domain(user, domain) do
-    update_info(user, &User.Info.remove_from_domain_block(&1, domain))
-  end
+  def deactivate(user, status \\ true)
 
-  def deactivate_async(user, status \\ true) do
-    BackgroundWorker.enqueue("deactivate_user", %{"user_id" => user.id, "status" => status})
+  def deactivate(users, status) when is_list(users) do
+    Repo.transaction(fn ->
+      for user <- users, do: deactivate(user, status)
+    end)
   end
 
-  def deactivate(%User{} = user, status \\ true) do
-    with {:ok, user} <- update_info(user, &User.Info.set_activation_status(&1, status)) do
+  def deactivate(%User{} = user, status) do
+    with {:ok, user} <- set_activation_status(user, status) do
       Enum.each(get_followers(user), &invalidate_cache/1)
       Enum.each(get_friends(user), &update_follower_count/1)
 
@@ -1068,8 +1164,27 @@ defmodule Pleroma.User do
     end
   end
 
-  def update_notification_settings(%User{} = user, settings \\ %{}) do
-    update_info(user, &User.Info.update_notification_settings(&1, settings))
+  def update_notification_settings(%User{} = user, settings) do
+    settings =
+      settings
+      |> Enum.map(fn {k, v} -> {k, v in [true, "true", "True", "1"]} end)
+      |> Map.new()
+
+    notification_settings =
+      user.notification_settings
+      |> Map.merge(settings)
+      |> Map.take(["followers", "follows", "non_follows", "non_followers"])
+
+    params = %{notification_settings: notification_settings}
+
+    user
+    |> cast(params, [:notification_settings])
+    |> validate_required([:notification_settings])
+    |> update_and_set_cache()
+  end
+
+  def delete(users) when is_list(users) do
+    for user <- users, do: delete(user)
   end
 
   def delete(%User{} = user) do
@@ -1107,7 +1222,7 @@ defmodule Pleroma.User do
     pages = Pleroma.Config.get!([:fetch_initial_posts, :pages])
 
     # Insert all the posts in reverse order, so they're in the right order on the timeline
-    user.info.source_data["outbox"]
+    user.source_data["outbox"]
     |> Utils.fetch_ordered_collection(pages)
     |> Enum.reverse()
     |> Enum.each(&Pleroma.Web.Federator.incoming_ap_doc/1)
@@ -1228,24 +1343,13 @@ defmodule Pleroma.User do
 
   defp delete_activity(_activity), do: "Doing nothing"
 
-  def html_filter_policy(%User{info: %{no_rich_text: true}}) do
+  def html_filter_policy(%User{no_rich_text: true}) do
     Pleroma.HTML.Scrubber.TwitterText
   end
 
   def html_filter_policy(_), do: Pleroma.Config.get([:markup, :scrub_policy])
 
-  def fetch_by_ap_id(ap_id) do
-    case ActivityPub.make_user_from_ap_id(ap_id) do
-      {:ok, user} ->
-        {:ok, user}
-
-      _ ->
-        case OStatus.make_user(ap_id) do
-          {:ok, user} -> {:ok, user}
-          _ -> {:error, "Could not fetch by AP id"}
-        end
-    end
-  end
+  def fetch_by_ap_id(ap_id), do: ActivityPub.make_user_from_ap_id(ap_id)
 
   def get_or_fetch_by_ap_id(ap_id) do
     user = get_cached_by_ap_id(ap_id)
@@ -1275,7 +1379,7 @@ defmodule Pleroma.User do
     else
       _ ->
         {:ok, user} =
-          %User{info: %User.Info{}}
+          %User{}
           |> cast(%{}, [:ap_id, :nickname, :local])
           |> put_change(:ap_id, uri)
           |> put_change(:nickname, nickname)
@@ -1288,9 +1392,7 @@ defmodule Pleroma.User do
   end
 
   # AP style
-  def public_key_from_info(%{
-        source_data: %{"publicKey" => %{"publicKeyPem" => public_key_pem}}
-      }) do
+  def public_key(%{source_data: %{"publicKey" => %{"publicKeyPem" => public_key_pem}}}) do
     key =
       public_key_pem
       |> :public_key.pem_decode()
@@ -1300,16 +1402,11 @@ defmodule Pleroma.User do
     {:ok, key}
   end
 
-  # OStatus Magic Key
-  def public_key_from_info(%{magic_key: magic_key}) when not is_nil(magic_key) do
-    {:ok, Pleroma.Web.Salmon.decode_key(magic_key)}
-  end
-
-  def public_key_from_info(_), do: {:error, "not found key"}
+  def public_key(_), do: {:error, "not found key"}
 
   def get_public_key_for_ap_id(ap_id) do
     with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id),
-         {:ok, public_key} <- public_key_from_info(user.info) do
+         {:ok, public_key} <- public_key(user) do
       {:ok, public_key}
     else
       _ -> :error
@@ -1328,7 +1425,7 @@ defmodule Pleroma.User do
   end
 
   def ap_enabled?(%User{local: true}), do: true
-  def ap_enabled?(%User{info: info}), do: info.ap_enabled
+  def ap_enabled?(%User{ap_enabled: ap_enabled}), do: ap_enabled
   def ap_enabled?(_), do: false
 
   @doc "Gets or fetch a user by uri or nickname."
@@ -1441,7 +1538,6 @@ defmodule Pleroma.User do
     %User{
       name: ap_id,
       ap_id: ap_id,
-      info: %User.Info{},
       nickname: "erroruser@example.com",
       inserted_at: NaiveDateTime.utc_now()
     }
@@ -1454,7 +1550,7 @@ defmodule Pleroma.User do
   end
 
   def showing_reblogs?(%User{} = user, %User{} = target) do
-    target.ap_id not in user.info.muted_reblogs
+    target.ap_id not in user.muted_reblogs
   end
 
   @doc """
@@ -1486,7 +1582,7 @@ defmodule Pleroma.User do
       left_join: a in Pleroma.Activity,
       on: u.ap_id == a.actor,
       where: not is_nil(u.nickname),
-      where: fragment("not (?->'deactivated' @> 'true')", u.info),
+      where: u.deactivated != ^true,
       where: u.id not in ^has_read_notifications,
       group_by: u.id,
       having:
@@ -1500,16 +1596,16 @@ defmodule Pleroma.User do
 
   ## Examples
 
-      iex> Pleroma.User.switch_email_notifications(Pleroma.User{info: %{email_notifications: %{"digest" => false}}}, "digest", true)
-      Pleroma.User{info: %{email_notifications: %{"digest" => true}}}
+      iex> Pleroma.User.switch_email_notifications(Pleroma.User{email_notifications: %{"digest" => false}}, "digest", true)
+      Pleroma.User{email_notifications: %{"digest" => true}}
 
-      iex> Pleroma.User.switch_email_notifications(Pleroma.User{info: %{email_notifications: %{"digest" => true}}}, "digest", false)
-      Pleroma.User{info: %{email_notifications: %{"digest" => false}}}
+      iex> Pleroma.User.switch_email_notifications(Pleroma.User{email_notifications: %{"digest" => true}}, "digest", false)
+      Pleroma.User{email_notifications: %{"digest" => false}}
   """
   @spec switch_email_notifications(t(), String.t(), boolean()) ::
           {:ok, t()} | {:error, Ecto.Changeset.t()}
   def switch_email_notifications(user, type, status) do
-    update_info(user, &User.Info.update_email_notifications(&1, %{type => status}))
+    User.update_email_notifications(user, %{type => status})
   end
 
   @doc """
@@ -1529,17 +1625,16 @@ defmodule Pleroma.User do
 
   @spec toggle_confirmation(User.t()) :: {:ok, User.t()} | {:error, Changeset.t()}
   def toggle_confirmation(%User{} = user) do
-    need_confirmation? = !user.info.confirmation_pending
-
     user
-    |> update_info(&User.Info.confirmation_changeset(&1, need_confirmation: need_confirmation?))
+    |> confirmation_changeset(need_confirmation: !user.confirmation_pending)
+    |> update_and_set_cache()
   end
 
-  def get_mascot(%{info: %{mascot: %{} = mascot}}) when not is_nil(mascot) do
+  def get_mascot(%{mascot: %{} = mascot}) when not is_nil(mascot) do
     mascot
   end
 
-  def get_mascot(%{info: %{mascot: mascot}}) when is_nil(mascot) do
+  def get_mascot(%{mascot: mascot}) when is_nil(mascot) do
     # use instance-default
     config = Pleroma.Config.get([:assets, :mascots])
     default_mascot = Pleroma.Config.get([:assets, :default_mascot])
@@ -1609,25 +1704,285 @@ defmodule Pleroma.User do
     |> update_and_set_cache()
   end
 
-  @doc """
-  Changes `user.info` and returns the user changeset.
+  # Internal function; public one is `deactivate/2`
+  defp set_activation_status(user, deactivated) do
+    user
+    |> cast(%{deactivated: deactivated}, [:deactivated])
+    |> update_and_set_cache()
+  end
 
-  `fun` is called with the `user.info`.
-  """
-  def change_info(user, fun) do
-    changeset = change(user)
-    info = get_field(changeset, :info) || %User.Info{}
-    put_embed(changeset, :info, fun.(info))
+  def update_banner(user, banner) do
+    user
+    |> cast(%{banner: banner}, [:banner])
+    |> update_and_set_cache()
   end
 
-  @doc """
-  Updates `user.info` and sets cache.
+  def update_background(user, background) do
+    user
+    |> cast(%{background: background}, [:background])
+    |> update_and_set_cache()
+  end
+
+  def update_source_data(user, source_data) do
+    user
+    |> cast(%{source_data: source_data}, [:source_data])
+    |> update_and_set_cache()
+  end
+
+  def roles(%{is_moderator: is_moderator, is_admin: is_admin}) do
+    %{
+      admin: is_admin,
+      moderator: is_moderator
+    }
+  end
+
+  # ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``.
+  # For example: [{"name": "Pronoun", "value": "she/her"}, …]
+  def fields(%{fields: nil, source_data: %{"attachment" => attachment}}) do
+    limit = Pleroma.Config.get([:instance, :max_remote_account_fields], 0)
+
+    attachment
+    |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
+    |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
+    |> Enum.take(limit)
+  end
+
+  def fields(%{fields: nil}), do: []
+
+  def fields(%{fields: fields}), do: fields
+
+  def validate_fields(changeset, remote? \\ false) do
+    limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
+    limit = Pleroma.Config.get([:instance, limit_name], 0)
+
+    changeset
+    |> validate_length(:fields, max: limit)
+    |> validate_change(:fields, fn :fields, fields ->
+      if Enum.all?(fields, &valid_field?/1) do
+        []
+      else
+        [fields: "invalid"]
+      end
+    end)
+  end
+
+  defp valid_field?(%{"name" => name, "value" => value}) do
+    name_limit = Pleroma.Config.get([:instance, :account_field_name_length], 255)
+    value_limit = Pleroma.Config.get([:instance, :account_field_value_length], 255)
+
+    is_binary(name) && is_binary(value) && String.length(name) <= name_limit &&
+      String.length(value) <= value_limit
+  end
+
+  defp valid_field?(_), do: false
+
+  defp truncate_field(%{"name" => name, "value" => value}) do
+    {name, _chopped} =
+      String.split_at(name, Pleroma.Config.get([:instance, :account_field_name_length], 255))
+
+    {value, _chopped} =
+      String.split_at(value, Pleroma.Config.get([:instance, :account_field_value_length], 255))
+
+    %{"name" => name, "value" => value}
+  end
+
+  def admin_api_update(user, params) do
+    user
+    |> cast(params, [
+      :is_moderator,
+      :is_admin,
+      :show_role
+    ])
+    |> update_and_set_cache()
+  end
+
+  def mascot_update(user, url) do
+    user
+    |> cast(%{mascot: url}, [:mascot])
+    |> validate_required([:mascot])
+    |> update_and_set_cache()
+  end
+
+  def mastodon_settings_update(user, settings) do
+    user
+    |> cast(%{settings: settings}, [:settings])
+    |> validate_required([:settings])
+    |> update_and_set_cache()
+  end
+
+  @spec confirmation_changeset(User.t(), keyword()) :: Changeset.t()
+  def confirmation_changeset(user, need_confirmation: need_confirmation?) do
+    params =
+      if need_confirmation? do
+        %{
+          confirmation_pending: true,
+          confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64()
+        }
+      else
+        %{
+          confirmation_pending: false,
+          confirmation_token: nil
+        }
+      end
+
+    cast(user, params, [:confirmation_pending, :confirmation_token])
+  end
+
+  def add_pinnned_activity(user, %Pleroma.Activity{id: id}) do
+    if id not in user.pinned_activities do
+      max_pinned_statuses = Pleroma.Config.get([:instance, :max_pinned_statuses], 0)
+      params = %{pinned_activities: user.pinned_activities ++ [id]}
+
+      user
+      |> cast(params, [:pinned_activities])
+      |> validate_length(:pinned_activities,
+        max: max_pinned_statuses,
+        message: "You have already pinned the maximum number of statuses"
+      )
+    else
+      change(user)
+    end
+    |> update_and_set_cache()
+  end
+
+  def remove_pinnned_activity(user, %Pleroma.Activity{id: id}) do
+    params = %{pinned_activities: List.delete(user.pinned_activities, id)}
+
+    user
+    |> cast(params, [:pinned_activities])
+    |> update_and_set_cache()
+  end
+
+  def update_email_notifications(user, settings) do
+    email_notifications =
+      user.email_notifications
+      |> Map.merge(settings)
+      |> Map.take(["digest"])
+
+    params = %{email_notifications: email_notifications}
+    fields = [:email_notifications]
+
+    user
+    |> cast(params, fields)
+    |> validate_required(fields)
+    |> update_and_set_cache()
+  end
+
+  defp set_subscribers(user, subscribers) do
+    params = %{subscribers: subscribers}
+
+    user
+    |> cast(params, [:subscribers])
+    |> validate_required([:subscribers])
+    |> update_and_set_cache()
+  end
+
+  def add_to_subscribers(user, subscribed) do
+    set_subscribers(user, Enum.uniq([subscribed | user.subscribers]))
+  end
+
+  def remove_from_subscribers(user, subscribed) do
+    set_subscribers(user, List.delete(user.subscribers, subscribed))
+  end
+
+  defp set_domain_blocks(user, domain_blocks) do
+    params = %{domain_blocks: domain_blocks}
+
+    user
+    |> cast(params, [:domain_blocks])
+    |> validate_required([:domain_blocks])
+    |> update_and_set_cache()
+  end
+
+  def block_domain(user, domain_blocked) do
+    set_domain_blocks(user, Enum.uniq([domain_blocked | user.domain_blocks]))
+  end
+
+  def unblock_domain(user, domain_blocked) do
+    set_domain_blocks(user, List.delete(user.domain_blocks, domain_blocked))
+  end
+
+  defp set_blocks(user, blocks) do
+    params = %{blocks: blocks}
+
+    user
+    |> cast(params, [:blocks])
+    |> validate_required([:blocks])
+    |> update_and_set_cache()
+  end
+
+  def add_to_block(user, blocked) do
+    set_blocks(user, Enum.uniq([blocked | user.blocks]))
+  end
+
+  def remove_from_block(user, blocked) do
+    set_blocks(user, List.delete(user.blocks, blocked))
+  end
+
+  defp set_mutes(user, mutes) do
+    params = %{mutes: mutes}
+
+    user
+    |> cast(params, [:mutes])
+    |> validate_required([:mutes])
+    |> update_and_set_cache()
+  end
+
+  def add_to_mutes(user, muted, notifications?) do
+    with {:ok, user} <- set_mutes(user, Enum.uniq([muted | user.mutes])) do
+      set_notification_mutes(
+        user,
+        Enum.uniq([muted | user.muted_notifications]),
+        notifications?
+      )
+    end
+  end
+
+  def remove_from_mutes(user, muted) do
+    with {:ok, user} <- set_mutes(user, List.delete(user.mutes, muted)) do
+      set_notification_mutes(
+        user,
+        List.delete(user.muted_notifications, muted),
+        true
+      )
+    end
+  end
+
+  defp set_notification_mutes(user, _muted_notifications, false = _notifications?) do
+    {:ok, user}
+  end
+
+  defp set_notification_mutes(user, muted_notifications, true = _notifications?) do
+    params = %{muted_notifications: muted_notifications}
+
+    user
+    |> cast(params, [:muted_notifications])
+    |> validate_required([:muted_notifications])
+    |> update_and_set_cache()
+  end
+
+  def add_reblog_mute(user, ap_id) do
+    params = %{muted_reblogs: user.muted_reblogs ++ [ap_id]}
+
+    user
+    |> cast(params, [:muted_reblogs])
+    |> update_and_set_cache()
+  end
+
+  def remove_reblog_mute(user, ap_id) do
+    params = %{muted_reblogs: List.delete(user.muted_reblogs, ap_id)}
+
+    user
+    |> cast(params, [:muted_reblogs])
+    |> update_and_set_cache()
+  end
+
+  def set_invisible(user, invisible) do
+    params = %{invisible: invisible}
 
-  `fun` is called with the `user.info`.
-  """
-  def update_info(user, fun) do
     user
-    |> change_info(fun)
+    |> cast(params, [:invisible])
+    |> validate_required([:invisible])
     |> update_and_set_cache()
   end
 end
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
deleted file mode 100644 (file)
index 4b5b43d..0000000
+++ /dev/null
@@ -1,478 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.User.Info do
-  use Ecto.Schema
-  import Ecto.Changeset
-
-  alias Pleroma.User.Info
-
-  @type t :: %__MODULE__{}
-
-  embedded_schema do
-    field(:banner, :map, default: %{})
-    field(:background, :map, default: %{})
-    field(:source_data, :map, default: %{})
-    field(:note_count, :integer, default: 0)
-    field(:follower_count, :integer, default: 0)
-    # Should be filled in only for remote users
-    field(:following_count, :integer, default: nil)
-    field(:locked, :boolean, default: false)
-    field(:confirmation_pending, :boolean, default: false)
-    field(:password_reset_pending, :boolean, default: false)
-    field(:confirmation_token, :string, default: nil)
-    field(:default_scope, :string, default: "public")
-    field(:blocks, {:array, :string}, default: [])
-    field(:domain_blocks, {:array, :string}, default: [])
-    field(:mutes, {:array, :string}, default: [])
-    field(:muted_reblogs, {:array, :string}, default: [])
-    field(:muted_notifications, {:array, :string}, default: [])
-    field(:subscribers, {:array, :string}, default: [])
-    field(:deactivated, :boolean, default: false)
-    field(:no_rich_text, :boolean, default: false)
-    field(:ap_enabled, :boolean, default: false)
-    field(:is_moderator, :boolean, default: false)
-    field(:is_admin, :boolean, default: false)
-    field(:show_role, :boolean, default: true)
-    field(:keys, :string, default: nil)
-    field(:settings, :map, default: nil)
-    field(:magic_key, :string, default: nil)
-    field(:uri, :string, default: nil)
-    field(:topic, :string, default: nil)
-    field(:hub, :string, default: nil)
-    field(:salmon, :string, default: nil)
-    field(:hide_followers_count, :boolean, default: false)
-    field(:hide_follows_count, :boolean, default: false)
-    field(:hide_followers, :boolean, default: false)
-    field(:hide_follows, :boolean, default: false)
-    field(:hide_favorites, :boolean, default: true)
-    field(:unread_conversation_count, :integer, default: 0)
-    field(:pinned_activities, {:array, :string}, default: [])
-    field(:email_notifications, :map, default: %{"digest" => false})
-    field(:mascot, :map, default: nil)
-    field(:emoji, {:array, :map}, default: [])
-    field(:pleroma_settings_store, :map, default: %{})
-    field(:fields, {:array, :map}, default: nil)
-    field(:raw_fields, {:array, :map}, default: [])
-    field(:discoverable, :boolean, default: false)
-
-    field(:notification_settings, :map,
-      default: %{
-        "followers" => true,
-        "follows" => true,
-        "non_follows" => true,
-        "non_followers" => true
-      }
-    )
-
-    field(:skip_thread_containment, :boolean, default: false)
-
-    # Found in the wild
-    # ap_id -> Where is this used?
-    # bio -> Where is this used?
-    # avatar -> Where is this used?
-    # fqn -> Where is this used?
-    # host -> Where is this used?
-    # subject _> Where is this used?
-  end
-
-  def set_activation_status(info, deactivated) do
-    params = %{deactivated: deactivated}
-
-    info
-    |> cast(params, [:deactivated])
-    |> validate_required([:deactivated])
-  end
-
-  def set_password_reset_pending(info, pending) do
-    params = %{password_reset_pending: pending}
-
-    info
-    |> cast(params, [:password_reset_pending])
-    |> validate_required([:password_reset_pending])
-  end
-
-  def update_notification_settings(info, settings) do
-    settings =
-      settings
-      |> Enum.map(fn {k, v} -> {k, v in [true, "true", "True", "1"]} end)
-      |> Map.new()
-
-    notification_settings =
-      info.notification_settings
-      |> Map.merge(settings)
-      |> Map.take(["followers", "follows", "non_follows", "non_followers"])
-
-    params = %{notification_settings: notification_settings}
-
-    info
-    |> cast(params, [:notification_settings])
-    |> validate_required([:notification_settings])
-  end
-
-  @doc """
-  Update email notifications in the given User.Info struct.
-
-  Examples:
-
-      iex> update_email_notifications(%Pleroma.User.Info{email_notifications: %{"digest" => false}}, %{"digest" => true})
-      %Pleroma.User.Info{email_notifications: %{"digest" => true}}
-
-  """
-  @spec update_email_notifications(t(), map()) :: Ecto.Changeset.t()
-  def update_email_notifications(info, settings) do
-    email_notifications =
-      info.email_notifications
-      |> Map.merge(settings)
-      |> Map.take(["digest"])
-
-    params = %{email_notifications: email_notifications}
-    fields = [:email_notifications]
-
-    info
-    |> cast(params, fields)
-    |> validate_required(fields)
-  end
-
-  def add_to_note_count(info, number) do
-    set_note_count(info, info.note_count + number)
-  end
-
-  def set_note_count(info, number) do
-    params = %{note_count: Enum.max([0, number])}
-
-    info
-    |> cast(params, [:note_count])
-    |> validate_required([:note_count])
-  end
-
-  def set_follower_count(info, number) do
-    params = %{follower_count: Enum.max([0, number])}
-
-    info
-    |> cast(params, [:follower_count])
-    |> validate_required([:follower_count])
-  end
-
-  def set_mutes(info, mutes) do
-    params = %{mutes: mutes}
-
-    info
-    |> cast(params, [:mutes])
-    |> validate_required([:mutes])
-  end
-
-  @spec set_notification_mutes(Changeset.t(), [String.t()], boolean()) :: Changeset.t()
-  def set_notification_mutes(changeset, muted_notifications, notifications?) do
-    if notifications? do
-      put_change(changeset, :muted_notifications, muted_notifications)
-      |> validate_required([:muted_notifications])
-    else
-      changeset
-    end
-  end
-
-  def set_blocks(info, blocks) do
-    params = %{blocks: blocks}
-
-    info
-    |> cast(params, [:blocks])
-    |> validate_required([:blocks])
-  end
-
-  def set_subscribers(info, subscribers) do
-    params = %{subscribers: subscribers}
-
-    info
-    |> cast(params, [:subscribers])
-    |> validate_required([:subscribers])
-  end
-
-  @spec add_to_mutes(Info.t(), String.t(), boolean()) :: Changeset.t()
-  def add_to_mutes(info, muted, notifications?) do
-    info
-    |> set_mutes(Enum.uniq([muted | info.mutes]))
-    |> set_notification_mutes(
-      Enum.uniq([muted | info.muted_notifications]),
-      notifications?
-    )
-  end
-
-  @spec remove_from_mutes(Info.t(), String.t()) :: Changeset.t()
-  def remove_from_mutes(info, muted) do
-    info
-    |> set_mutes(List.delete(info.mutes, muted))
-    |> set_notification_mutes(List.delete(info.muted_notifications, muted), true)
-  end
-
-  def add_to_block(info, blocked) do
-    set_blocks(info, Enum.uniq([blocked | info.blocks]))
-  end
-
-  def remove_from_block(info, blocked) do
-    set_blocks(info, List.delete(info.blocks, blocked))
-  end
-
-  def add_to_subscribers(info, subscribed) do
-    set_subscribers(info, Enum.uniq([subscribed | info.subscribers]))
-  end
-
-  def remove_from_subscribers(info, subscribed) do
-    set_subscribers(info, List.delete(info.subscribers, subscribed))
-  end
-
-  def set_domain_blocks(info, domain_blocks) do
-    params = %{domain_blocks: domain_blocks}
-
-    info
-    |> cast(params, [:domain_blocks])
-    |> validate_required([:domain_blocks])
-  end
-
-  def add_to_domain_block(info, domain_blocked) do
-    set_domain_blocks(info, Enum.uniq([domain_blocked | info.domain_blocks]))
-  end
-
-  def remove_from_domain_block(info, domain_blocked) do
-    set_domain_blocks(info, List.delete(info.domain_blocks, domain_blocked))
-  end
-
-  def set_keys(info, keys) do
-    params = %{keys: keys}
-
-    info
-    |> cast(params, [:keys])
-    |> validate_required([:keys])
-  end
-
-  def remote_user_creation(info, params) do
-    params =
-      if Map.has_key?(params, :fields) do
-        Map.put(params, :fields, Enum.map(params[:fields], &truncate_field/1))
-      else
-        params
-      end
-
-    info
-    |> cast(params, [
-      :ap_enabled,
-      :source_data,
-      :banner,
-      :locked,
-      :magic_key,
-      :uri,
-      :hub,
-      :topic,
-      :salmon,
-      :hide_followers,
-      :hide_follows,
-      :hide_followers_count,
-      :hide_follows_count,
-      :follower_count,
-      :fields,
-      :following_count,
-      :discoverable
-    ])
-    |> validate_fields(true)
-  end
-
-  def user_upgrade(info, params, remote? \\ false) do
-    info
-    |> cast(params, [
-      :ap_enabled,
-      :source_data,
-      :banner,
-      :locked,
-      :magic_key,
-      :follower_count,
-      :following_count,
-      :hide_follows,
-      :fields,
-      :hide_followers,
-      :discoverable,
-      :hide_followers_count,
-      :hide_follows_count
-    ])
-    |> validate_fields(remote?)
-  end
-
-  def profile_update(info, params) do
-    info
-    |> cast(params, [
-      :locked,
-      :no_rich_text,
-      :default_scope,
-      :banner,
-      :hide_follows,
-      :hide_followers,
-      :hide_followers_count,
-      :hide_follows_count,
-      :hide_favorites,
-      :background,
-      :show_role,
-      :skip_thread_containment,
-      :fields,
-      :raw_fields,
-      :pleroma_settings_store,
-      :discoverable
-    ])
-    |> validate_fields()
-  end
-
-  def validate_fields(changeset, remote? \\ false) do
-    limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
-    limit = Pleroma.Config.get([:instance, limit_name], 0)
-
-    changeset
-    |> validate_length(:fields, max: limit)
-    |> validate_change(:fields, fn :fields, fields ->
-      if Enum.all?(fields, &valid_field?/1) do
-        []
-      else
-        [fields: "invalid"]
-      end
-    end)
-  end
-
-  defp valid_field?(%{"name" => name, "value" => value}) do
-    name_limit = Pleroma.Config.get([:instance, :account_field_name_length], 255)
-    value_limit = Pleroma.Config.get([:instance, :account_field_value_length], 255)
-
-    is_binary(name) && is_binary(value) && String.length(name) <= name_limit &&
-      String.length(value) <= value_limit
-  end
-
-  defp valid_field?(_), do: false
-
-  defp truncate_field(%{"name" => name, "value" => value}) do
-    {name, _chopped} =
-      String.split_at(name, Pleroma.Config.get([:instance, :account_field_name_length], 255))
-
-    {value, _chopped} =
-      String.split_at(value, Pleroma.Config.get([:instance, :account_field_value_length], 255))
-
-    %{"name" => name, "value" => value}
-  end
-
-  @spec confirmation_changeset(Info.t(), keyword()) :: Changeset.t()
-  def confirmation_changeset(info, opts) do
-    need_confirmation? = Keyword.get(opts, :need_confirmation)
-
-    params =
-      if need_confirmation? do
-        %{
-          confirmation_pending: true,
-          confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64()
-        }
-      else
-        %{
-          confirmation_pending: false,
-          confirmation_token: nil
-        }
-      end
-
-    cast(info, params, [:confirmation_pending, :confirmation_token])
-  end
-
-  def mastodon_settings_update(info, settings) do
-    params = %{settings: settings}
-
-    info
-    |> cast(params, [:settings])
-    |> validate_required([:settings])
-  end
-
-  def mascot_update(info, url) do
-    params = %{mascot: url}
-
-    info
-    |> cast(params, [:mascot])
-    |> validate_required([:mascot])
-  end
-
-  def set_source_data(info, source_data) do
-    params = %{source_data: source_data}
-
-    info
-    |> cast(params, [:source_data])
-    |> validate_required([:source_data])
-  end
-
-  def admin_api_update(info, params) do
-    info
-    |> cast(params, [
-      :is_moderator,
-      :is_admin,
-      :show_role
-    ])
-  end
-
-  def add_pinnned_activity(info, %Pleroma.Activity{id: id}) do
-    if id not in info.pinned_activities do
-      max_pinned_statuses = Pleroma.Config.get([:instance, :max_pinned_statuses], 0)
-      params = %{pinned_activities: info.pinned_activities ++ [id]}
-
-      info
-      |> cast(params, [:pinned_activities])
-      |> validate_length(:pinned_activities,
-        max: max_pinned_statuses,
-        message: "You have already pinned the maximum number of statuses"
-      )
-    else
-      change(info)
-    end
-  end
-
-  def remove_pinnned_activity(info, %Pleroma.Activity{id: id}) do
-    params = %{pinned_activities: List.delete(info.pinned_activities, id)}
-
-    cast(info, params, [:pinned_activities])
-  end
-
-  def roles(%Info{is_moderator: is_moderator, is_admin: is_admin}) do
-    %{
-      admin: is_admin,
-      moderator: is_moderator
-    }
-  end
-
-  def add_reblog_mute(info, ap_id) do
-    params = %{muted_reblogs: info.muted_reblogs ++ [ap_id]}
-
-    cast(info, params, [:muted_reblogs])
-  end
-
-  def remove_reblog_mute(info, ap_id) do
-    params = %{muted_reblogs: List.delete(info.muted_reblogs, ap_id)}
-
-    cast(info, params, [:muted_reblogs])
-  end
-
-  # ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``.
-  # For example: [{"name": "Pronoun", "value": "she/her"}, …]
-  def fields(%{fields: nil, source_data: %{"attachment" => attachment}}) do
-    limit = Pleroma.Config.get([:instance, :max_remote_account_fields], 0)
-
-    attachment
-    |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
-    |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
-    |> Enum.take(limit)
-  end
-
-  def fields(%{fields: nil}), do: []
-
-  def fields(%{fields: fields}), do: fields
-
-  def follow_information_update(info, params) do
-    info
-    |> cast(params, [
-      :hide_followers,
-      :hide_follows,
-      :follower_count,
-      :following_count,
-      :hide_followers_count,
-      :hide_follows_count
-    ])
-  end
-end
index 2baf016cfd3f3ecb7f269eb8f6e7faa0e92b63da..7f5273c4ea1cb3601bc75698b8799ea4d473ae3b 100644 (file)
@@ -56,7 +56,6 @@ defmodule Pleroma.User.Query do
 
   @ilike_criteria [:nickname, :name, :query]
   @equal_criteria [:email]
-  @role_criteria [:is_admin, :is_moderator]
   @contains_criteria [:ap_id, :nickname]
 
   @spec build(criteria()) :: Query.t()
@@ -100,15 +99,19 @@ defmodule Pleroma.User.Query do
     Enum.reduce(tags, query, &prepare_tag_criteria/2)
   end
 
-  defp compose_query({key, _}, query) when key in @role_criteria do
-    where(query, [u], fragment("(?->? @> 'true')", u.info, ^to_string(key)))
+  defp compose_query({:is_admin, _}, query) do
+    where(query, [u], u.is_admin)
+  end
+
+  defp compose_query({:is_moderator, _}, query) do
+    where(query, [u], u.is_moderator)
   end
 
   defp compose_query({:super_users, _}, query) do
     where(
       query,
       [u],
-      fragment("?->'is_admin' @> 'true' OR ?->'is_moderator' @> 'true'", u.info, u.info)
+      u.is_admin or u.is_moderator
     )
   end
 
@@ -117,7 +120,13 @@ defmodule Pleroma.User.Query do
   defp compose_query({:external, _}, query), do: location_query(query, false)
 
   defp compose_query({:active, _}, query) do
-    where(query, [u], fragment("not (?->'deactivated' @> 'true')", u.info))
+    User.restrict_deactivated(query)
+    |> where([u], not is_nil(u.nickname))
+  end
+
+  defp compose_query({:legacy_active, _}, query) do
+    query
+    |> where([u], fragment("not (?->'deactivated' @> 'true')", u.info))
     |> where([u], not is_nil(u.nickname))
   end
 
@@ -126,7 +135,7 @@ defmodule Pleroma.User.Query do
   end
 
   defp compose_query({:deactivated, true}, query) do
-    where(query, [u], fragment("?->'deactivated' @> 'true'", u.info))
+    where(query, [u], u.deactivated == ^true)
     |> where([u], not is_nil(u.nickname))
   end
 
index 6fb2c2352f8aa9655f635ffc687ae40fb26f95ad..bab8d92e2be299f78101a209219fdb7d4f6a31ab 100644 (file)
@@ -4,11 +4,9 @@
 
 defmodule Pleroma.User.Search do
   alias Pleroma.Pagination
-  alias Pleroma.Repo
   alias Pleroma.User
   import Ecto.Query
 
-  @similarity_threshold 0.25
   @limit 20
 
   def search(query_string, opts \\ []) do
@@ -23,18 +21,10 @@ defmodule Pleroma.User.Search do
 
     maybe_resolve(resolve, for_user, query_string)
 
-    {:ok, results} =
-      Repo.transaction(fn ->
-        Ecto.Adapters.SQL.query(
-          Repo,
-          "select set_limit(#{@similarity_threshold})",
-          []
-        )
-
-        query_string
-        |> search_query(for_user, following)
-        |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset)
-      end)
+    results =
+      query_string
+      |> search_query(for_user, following)
+      |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset)
 
     results
   end
@@ -56,26 +46,76 @@ defmodule Pleroma.User.Search do
     |> base_query(following)
     |> filter_blocked_user(for_user)
     |> filter_blocked_domains(for_user)
-    |> search_subqueries(query_string)
-    |> union_subqueries
-    |> distinct_query()
-    |> boost_search_rank_query(for_user)
+    |> fts_search(query_string)
+    |> trigram_rank(query_string)
+    |> boost_search_rank(for_user)
     |> subquery()
     |> order_by(desc: :search_rank)
     |> maybe_restrict_local(for_user)
   end
 
+  @nickname_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~\-@]+$/
+  defp fts_search(query, query_string) do
+    {nickname_weight, name_weight} =
+      if String.match?(query_string, @nickname_regex) do
+        {"A", "B"}
+      else
+        {"B", "A"}
+      end
+
+    query_string = to_tsquery(query_string)
+
+    from(
+      u in query,
+      where:
+        fragment(
+          """
+          (setweight(to_tsvector('simple', ?), ?) || setweight(to_tsvector('simple', ?), ?)) @@ to_tsquery('simple', ?)
+          """,
+          u.name,
+          ^name_weight,
+          u.nickname,
+          ^nickname_weight,
+          ^query_string
+        )
+    )
+  end
+
+  defp to_tsquery(query_string) do
+    String.trim_trailing(query_string, "@" <> local_domain())
+    |> String.replace(~r/[!-\/|@|[-`|{-~|:-?]+/, " ")
+    |> String.trim()
+    |> String.split()
+    |> Enum.map(&(&1 <> ":*"))
+    |> Enum.join(" | ")
+  end
+
+  defp trigram_rank(query, query_string) do
+    from(
+      u in query,
+      select_merge: %{
+        search_rank:
+          fragment(
+            "similarity(?, trim(? || ' ' || coalesce(?, '')))",
+            ^query_string,
+            u.nickname,
+            u.name
+          )
+      }
+    )
+  end
+
   defp base_query(_user, false), do: User
   defp base_query(user, true), do: User.get_followers_query(user)
 
-  defp filter_blocked_user(query, %User{info: %{blocks: blocks}})
+  defp filter_blocked_user(query, %User{blocks: blocks})
        when length(blocks) > 0 do
     from(q in query, where: not (q.ap_id in ^blocks))
   end
 
   defp filter_blocked_user(query, _), do: query
 
-  defp filter_blocked_domains(query, %User{info: %{domain_blocks: domain_blocks}})
+  defp filter_blocked_domains(query, %User{domain_blocks: domain_blocks})
        when length(domain_blocks) > 0 do
     domains = Enum.join(domain_blocks, ",")
 
@@ -87,21 +127,6 @@ defmodule Pleroma.User.Search do
 
   defp filter_blocked_domains(query, _), do: query
 
-  defp union_subqueries({fts_subquery, trigram_subquery}) do
-    from(s in trigram_subquery, union_all: ^fts_subquery)
-  end
-
-  defp search_subqueries(base_query, query_string) do
-    {
-      fts_search_subquery(base_query, query_string),
-      trigram_search_subquery(base_query, query_string)
-    }
-  end
-
-  defp distinct_query(q) do
-    from(s in subquery(q), order_by: s.search_type, distinct: s.id)
-  end
-
   defp maybe_resolve(true, user, query) do
     case {limit(), user} do
       {:all, _} -> :noop
@@ -126,9 +151,9 @@ defmodule Pleroma.User.Search do
 
   defp restrict_local(q), do: where(q, [u], u.local == true)
 
-  defp boost_search_rank_query(query, nil), do: query
+  defp local_domain, do: Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host])
 
-  defp boost_search_rank_query(query, for_user) do
+  defp boost_search_rank(query, %User{} = for_user) do
     friends_ids = User.get_friends_ids(for_user)
     followers_ids = User.get_followers_ids(for_user)
 
@@ -137,8 +162,8 @@ defmodule Pleroma.User.Search do
         search_rank:
           fragment(
             """
-             CASE WHEN (?) THEN 0.5 + (?) * 1.3
-             WHEN (?) THEN 0.5 + (?) * 1.2
+             CASE WHEN (?) THEN (?) * 1.5
+             WHEN (?) THEN (?) * 1.3
              WHEN (?) THEN (?) * 1.1
              ELSE (?) END
             """,
@@ -154,70 +179,5 @@ defmodule Pleroma.User.Search do
     )
   end
 
-  @spec fts_search_subquery(User.t() | Ecto.Query.t(), String.t()) :: Ecto.Query.t()
-  defp fts_search_subquery(query, term) do
-    processed_query =
-      String.trim_trailing(term, "@" <> local_domain())
-      |> String.replace(~r/[!-\/|@|[-`|{-~|:-?]+/, " ")
-      |> String.trim()
-      |> String.split()
-      |> Enum.map(&(&1 <> ":*"))
-      |> Enum.join(" | ")
-
-    from(
-      u in query,
-      select_merge: %{
-        search_type: ^0,
-        search_rank:
-          fragment(
-            """
-            ts_rank_cd(
-              setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') ||
-              setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B'),
-              to_tsquery('simple', ?),
-              32
-            )
-            """,
-            u.nickname,
-            u.name,
-            ^processed_query
-          )
-      },
-      where:
-        fragment(
-          """
-            (setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') ||
-            setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B')) @@ to_tsquery('simple', ?)
-          """,
-          u.nickname,
-          u.name,
-          ^processed_query
-        )
-    )
-    |> User.restrict_deactivated()
-  end
-
-  @spec trigram_search_subquery(User.t() | Ecto.Query.t(), String.t()) :: Ecto.Query.t()
-  defp trigram_search_subquery(query, term) do
-    term = String.trim_trailing(term, "@" <> local_domain())
-
-    from(
-      u in query,
-      select_merge: %{
-        # ^1 gives 'Postgrex expected a binary, got 1' for some weird reason
-        search_type: fragment("?", 1),
-        search_rank:
-          fragment(
-            "similarity(?, trim(? || ' ' || coalesce(?, '')))",
-            ^term,
-            u.nickname,
-            u.name
-          )
-      },
-      where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^term)
-    )
-    |> User.restrict_deactivated()
-  end
-
-  defp local_domain, do: Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host])
+  defp boost_search_rank(query, _for_user), do: query
 end
index 4cdf4876e57da1120a256459ca0a602b93c506f4..40f3d3781fede2fc4b39e8ef3fee3ad31cbb80dc 100644 (file)
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
   alias Pleroma.Activity.Ir.Topics
   alias Pleroma.Config
   alias Pleroma.Conversation
+  alias Pleroma.Conversation.Participation
   alias Pleroma.Notification
   alias Pleroma.Object
   alias Pleroma.Object.Containment
@@ -68,7 +69,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
   defp check_actor_is_active(actor) do
     if not is_nil(actor) do
       with user <- User.get_cached_by_ap_id(actor),
-           false <- user.info.deactivated do
+           false <- user.deactivated do
         true
       else
         _e -> false
@@ -131,7 +132,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
          {:ok, map} <- MRF.filter(map),
          {recipients, _, _} = get_recipients(map),
          {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
-         :ok <- Containment.contain_child(map),
+         {:containment, :ok} <- {:containment, Containment.contain_child(map)},
          {:ok, map, object} <- insert_full_object(map) do
       {:ok, activity} =
         Repo.insert(%Activity{
@@ -153,11 +154,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
       Notification.create_notifications(activity)
 
-      participations =
-        activity
-        |> Conversation.create_or_bump_for()
-        |> get_participations()
-
+      conversation = create_or_bump_conversation(activity, map["actor"])
+      participations = get_participations(conversation)
       stream_out(activity)
       stream_out_participations(participations)
       {:ok, activity}
@@ -182,7 +180,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     end
   end
 
-  defp get_participations({:ok, %{participations: participations}}), do: participations
+  defp create_or_bump_conversation(activity, actor) do
+    with {:ok, conversation} <- Conversation.create_or_bump_for(activity),
+         %User{} = user <- User.get_cached_by_ap_id(actor),
+         Participation.mark_as_read(user, conversation) do
+      {:ok, conversation}
+    end
+  end
+
+  defp get_participations({:ok, conversation}) do
+    conversation
+    |> Repo.preload(:participations, force: true)
+    |> Map.get(:participations)
+  end
+
   defp get_participations(_), do: []
 
   def stream_out_participations(participations) do
@@ -225,6 +236,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     # only accept false as false value
     local = !(params[:local] == false)
     published = params[:published]
+    quick_insert? = Pleroma.Config.get([:env]) == :benchmark
 
     with create_data <-
            make_create_data(
@@ -235,12 +247,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
          {:fake, false, activity} <- {:fake, fake, activity},
          _ <- increase_replies_count_if_reply(create_data),
          _ <- increase_poll_votes_if_vote(create_data),
-         # Changing note count prior to enqueuing federation task in order to avoid
-         # race conditions on updating user.info
+         {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
          {:ok, _actor} <- increase_note_count_if_public(actor, activity),
          :ok <- maybe_federate(activity) do
       {:ok, activity}
     else
+      {:quick_insert, true, activity} ->
+        {:ok, activity}
+
       {:fake, true, activity} ->
         {:ok, activity}
 
@@ -429,8 +443,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
          {:ok, activity} <- insert(data, local, false),
          stream_out_participations(object, user),
          _ <- decrease_replies_count_if_reply(object),
-         # Changing note count prior to enqueuing federation task in order to avoid
-         # race conditions on updating user.info
          {:ok, _actor} <- decrease_note_count_if_public(user, object),
          :ok <- maybe_federate(activity) do
       {:ok, activity}
@@ -645,7 +657,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
   defp restrict_thread_visibility(
          query,
-         %{"user" => %User{info: %{skip_thread_containment: true}}},
+         %{"user" => %User{skip_thread_containment: true}},
          _
        ),
        do: query
@@ -683,7 +695,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
       |> Map.put("user", reading_user)
       |> Map.put("actor_id", user.ap_id)
       |> Map.put("whole_db", true)
-      |> Map.put("pinned_activity_ids", user.info.pinned_activities)
+      |> Map.put("pinned_activity_ids", user.pinned_activities)
 
     recipients =
       user_activities_recipients(%{
@@ -844,8 +856,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
   defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
 
-  defp restrict_muted(query, %{"muting_user" => %User{info: info}} = opts) do
-    mutes = info.mutes
+  defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
+    mutes = user.mutes
 
     query =
       from([activity] in query,
@@ -862,9 +874,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
   defp restrict_muted(query, _), do: query
 
-  defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
-    blocks = info.blocks || []
-    domain_blocks = info.domain_blocks || []
+  defp restrict_blocked(query, %{"blocking_user" => %User{} = user}) do
+    blocks = user.blocks || []
+    domain_blocks = user.domain_blocks || []
 
     query =
       if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
@@ -905,8 +917,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
   defp restrict_pinned(query, _), do: query
 
-  defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
-    muted_reblogs = info.muted_reblogs || []
+  defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user}) do
+    muted_reblogs = user.muted_reblogs || []
 
     from(
       activity in query,
@@ -1091,17 +1103,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     locked = data["manuallyApprovesFollowers"] || false
     data = Transmogrifier.maybe_fix_user_object(data)
     discoverable = data["discoverable"] || false
+    invisible = data["invisible"] || false
 
     user_data = %{
       ap_id: data["id"],
-      info: %{
-        ap_enabled: true,
-        source_data: data,
-        banner: banner,
-        fields: fields,
-        locked: locked,
-        discoverable: discoverable
-      },
+      ap_enabled: true,
+      source_data: data,
+      banner: banner,
+      fields: fields,
+      locked: locked,
+      discoverable: discoverable,
+      invisible: invisible,
       avatar: avatar,
       name: data["name"],
       follower_address: data["followers"],
@@ -1153,7 +1165,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     with {:enabled, true} <-
            {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
          {:ok, info} <- fetch_follow_information_for_user(data) do
-      info = Map.merge(data.info, info)
+      info = Map.merge(data[:info] || %{}, info)
       Map.put(data, :info, info)
     else
       {:enabled, false} ->
@@ -1204,7 +1216,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
          data <- maybe_update_follow_information(data) do
       {:ok, data}
     else
-      e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
+      e ->
+        Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
+        {:error, e}
     end
   end
 
index 080030eb56a1c4d6a3834da71aa0ae9eecfe4314..56862331851e92fd1d7cd5e5da80779e76c1334a 100644 (file)
@@ -137,7 +137,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
     with %User{} = user <- User.get_cached_by_nickname(nickname),
          {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user),
          {:show_follows, true} <-
-           {:show_follows, (for_user && for_user == user) || !user.info.hide_follows} do
+           {:show_follows, (for_user && for_user == user) || !user.hide_follows} do
       {page, _} = Integer.parse(page)
 
       conn
@@ -174,7 +174,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
     with %User{} = user <- User.get_cached_by_nickname(nickname),
          {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user),
          {:show_followers, true} <-
-           {:show_followers, (for_user && for_user == user) || !user.info.hide_followers} do
+           {:show_followers, (for_user && for_user == user) || !user.hide_followers} do
       {page, _} = Integer.parse(page)
 
       conn
@@ -387,7 +387,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
 
   def handle_user_activity(user, %{"type" => "Delete"} = params) do
     with %Object{} = object <- Object.normalize(params["object"]),
-         true <- user.info.is_moderator || user.ap_id == object.data["actor"],
+         true <- user.is_moderator || user.ap_id == object.data["actor"],
          {:ok, delete} <- ActivityPub.delete(object) do
       {:ok, delete}
     else
index b90193ca03841f04c1cc3f8140056f7aec6a9f93..8abe18e29530db74d58b4d9766c209d4026b4109 100644 (file)
@@ -11,7 +11,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do
 
   # has the user successfully posted before?
   defp old_user?(%User{} = u) do
-    u.info.note_count > 0 || u.info.follower_count > 0
+    u.note_count > 0 || u.follower_count > 0
   end
 
   # does the post contain links?
index 3866daceee4b57a1e8ddf049aaa3b8653bb47cd5..4ea37fc7ba8f2d5944963c8e4957dd7766041957 100644 (file)
@@ -129,7 +129,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
           []
       end
 
-    Pleroma.Web.Salmon.remote_users(actor, activity) ++ followers ++ fetchers
+    Pleroma.Web.Federator.Publisher.remote_users(actor, activity) ++ followers ++ fetchers
   end
 
   defp get_cc_ap_ids(ap_id, recipients) do
@@ -140,7 +140,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
     |> Enum.map(& &1.ap_id)
   end
 
-  defp maybe_use_sharedinbox(%User{info: %{source_data: data}}),
+  defp maybe_use_sharedinbox(%User{source_data: data}),
     do: (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
 
   @doc """
@@ -156,7 +156,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
   """
   def determine_inbox(
         %Activity{data: activity_data},
-        %User{info: %{source_data: data}} = user
+        %User{source_data: data} = user
       ) do
     to = activity_data["to"] || []
     cc = activity_data["cc"] || []
@@ -190,12 +190,12 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
 
     recipients
     |> Enum.filter(&User.ap_enabled?/1)
-    |> Enum.map(fn %{info: %{source_data: data}} -> data["inbox"] end)
+    |> Enum.map(fn %{source_data: data} -> data["inbox"] end)
     |> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
     |> Instances.filter_reachable()
     |> Enum.each(fn {inbox, unreachable_since} ->
       %User{ap_id: ap_id} =
-        Enum.find(recipients, fn %{info: %{source_data: data}} -> data["inbox"] == inbox end)
+        Enum.find(recipients, fn %{source_data: data} -> data["inbox"] == inbox end)
 
       # Get all the recipients on the same host and add them to cc. Otherwise, a remote
       # instance would only accept a first message for the first recipient and ignore the rest.
index c2ac38907dd8cd796878363248adbae74f6b7ac9..a9434d75c09000e6fad587ba37a6eb90c225f203 100644 (file)
@@ -10,8 +10,12 @@ defmodule Pleroma.Web.ActivityPub.Relay do
   require Logger
 
   def get_actor do
-    "#{Pleroma.Web.Endpoint.url()}/relay"
-    |> User.get_or_create_service_actor_by_ap_id()
+    actor =
+      "#{Pleroma.Web.Endpoint.url()}/relay"
+      |> User.get_or_create_service_actor_by_ap_id()
+
+    {:ok, actor} = User.set_invisible(actor, true)
+    actor
   end
 
   @spec follow(String.t()) :: {:ok, Activity.t()} | {:error, any()}
@@ -51,6 +55,20 @@ defmodule Pleroma.Web.ActivityPub.Relay do
 
   def publish(_), do: {:error, "Not implemented"}
 
+  @spec list() :: {:ok, [String.t()]} | {:error, any()}
+  def list do
+    with %User{following: following} = _user <- get_actor() do
+      list =
+        following
+        |> Enum.map(fn entry -> URI.parse(entry).host end)
+        |> Enum.uniq()
+
+      {:ok, list}
+    else
+      error -> format_error(error)
+    end
+  end
+
   defp format_error({:error, error}), do: format_error(error)
 
   defp format_error(error) do
index b56343beb619f330a195979a246b59517e807c54..9b3ee842ba9886b9e92845b8d0b80356597c96a1 100644 (file)
@@ -596,13 +596,18 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
           data,
         _options
       )
-      when object_type in ["Person", "Application", "Service", "Organization"] do
+      when object_type in [
+             "Person",
+             "Application",
+             "Service",
+             "Organization"
+           ] do
     with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do
       {:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
 
-      banner = new_user_data[:info][:banner]
-      locked = new_user_data[:info][:locked] || false
-      attachment = get_in(new_user_data, [:info, :source_data, "attachment"]) || []
+      locked = new_user_data[:locked] || false
+      attachment = get_in(new_user_data, [:source_data, "attachment"]) || []
+      invisible = new_user_data[:invisible] || false
 
       fields =
         attachment
@@ -611,8 +616,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
 
       update_data =
         new_user_data
-        |> Map.take([:name, :bio, :avatar])
-        |> Map.put(:info, %{banner: banner, locked: locked, fields: fields})
+        |> Map.take([:avatar, :banner, :bio, :name])
+        |> Map.put(:fields, fields)
+        |> Map.put(:locked, locked)
+        |> Map.put(:invisible, invisible)
 
       actor
       |> User.upgrade_changeset(update_data, true)
@@ -979,7 +986,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
     %{"type" => "Mention", "href" => ap_id, "name" => "@#{nickname}"}
   end
 
-  def take_emoji_tags(%User{info: %{emoji: emoji} = _user_info} = _user) do
+  def take_emoji_tags(%User{emoji: emoji}) do
     emoji
     |> Enum.flat_map(&Map.to_list/1)
     |> Enum.map(&build_emoji_tag/1)
@@ -1073,8 +1080,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
 
     Repo.update_all(q, [])
 
-    maybe_retire_websub(user.ap_id)
-
     q =
       from(
         a in Activity,
@@ -1117,19 +1122,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
     |> User.update_and_set_cache()
   end
 
-  def maybe_retire_websub(ap_id) do
-    # some sanity checks
-    if is_binary(ap_id) && String.length(ap_id) > 8 do
-      q =
-        from(
-          ws in Pleroma.Web.Websub.WebsubClientSubscription,
-          where: fragment("? like ?", ws.topic, ^"#{ap_id}%")
-        )
-
-      Repo.delete_all(q)
-    end
-  end
-
   def maybe_fix_user_url(%{"url" => url} = data) when is_map(url) do
     Map.put(data, "url", url["href"])
   end
index 520cc1b0ca545b6d7eac4e69b486cf7f106ec422..d812fd734292cf9d3b481cbfe2dc520517653a3f 100644 (file)
@@ -51,26 +51,28 @@ defmodule Pleroma.Web.ActivityPub.Utils do
 
   def determine_explicit_mentions(_), do: []
 
-  @spec recipient_in_collection(any(), any()) :: boolean()
-  defp recipient_in_collection(ap_id, coll) when is_binary(coll), do: ap_id == coll
-  defp recipient_in_collection(ap_id, coll) when is_list(coll), do: ap_id in coll
-  defp recipient_in_collection(_, _), do: false
+  @spec label_in_collection?(any(), any()) :: boolean()
+  defp label_in_collection?(ap_id, coll) when is_binary(coll), do: ap_id == coll
+  defp label_in_collection?(ap_id, coll) when is_list(coll), do: ap_id in coll
+  defp label_in_collection?(_, _), do: false
+
+  @spec label_in_message?(String.t(), map()) :: boolean()
+  def label_in_message?(label, params),
+    do:
+      [params["to"], params["cc"], params["bto"], params["bcc"]]
+      |> Enum.any?(&label_in_collection?(label, &1))
+
+  @spec unaddressed_message?(map()) :: boolean()
+  def unaddressed_message?(params),
+    do:
+      [params["to"], params["cc"], params["bto"], params["bcc"]]
+      |> Enum.all?(&is_nil(&1))
 
   @spec recipient_in_message(User.t(), User.t(), map()) :: boolean()
-  def recipient_in_message(%User{ap_id: ap_id} = recipient, %User{} = actor, params) do
-    addresses = [params["to"], params["cc"], params["bto"], params["bcc"]]
-
-    cond do
-      Enum.any?(addresses, &recipient_in_collection(ap_id, &1)) -> true
-      # if the message is unaddressed at all, then assume it is directly addressed
-      # to the recipient
-      Enum.all?(addresses, &is_nil(&1)) -> true
-      # if the message is sent from somebody the user is following, then assume it
-      # is addressed to the recipient
-      User.following?(recipient, actor) -> true
-      true -> false
-    end
-  end
+  def recipient_in_message(%User{ap_id: ap_id} = recipient, %User{} = actor, params),
+    do:
+      label_in_message?(ap_id, params) || unaddressed_message?(params) ||
+        User.following?(recipient, actor)
 
   defp extract_list(target) when is_binary(target), do: [target]
   defp extract_list(lst) when is_list(lst), do: lst
@@ -78,8 +80,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
 
   def maybe_splice_recipient(ap_id, params) do
     need_splice? =
-      !recipient_in_collection(ap_id, params["to"]) &&
-        !recipient_in_collection(ap_id, params["cc"])
+      !label_in_collection?(ap_id, params["to"]) &&
+        !label_in_collection?(ap_id, params["cc"])
 
     if need_splice? do
       cc_list = extract_list(params["cc"])
@@ -493,10 +495,14 @@ defmodule Pleroma.Web.ActivityPub.Utils do
         %Activity{data: %{"actor" => actor}},
         object
       ) do
-    announcements = take_announcements(object)
+    unless actor |> User.get_cached_by_ap_id() |> User.invisible?() do
+      announcements = take_announcements(object)
 
-    with announcements <- Enum.uniq([actor | announcements]) do
-      update_element_in_object("announcement", announcements, object)
+      with announcements <- Enum.uniq([actor | announcements]) do
+        update_element_in_object("announcement", announcements, object)
+      end
+    else
+      {:ok, object}
     end
   end
 
index 9b39d1629144d0f5f130d271ed2c58862a236a95..cf08045c978b024a0cc1fc95748bec677cddce9b 100644 (file)
@@ -55,7 +55,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
         "owner" => user.ap_id,
         "publicKeyPem" => public_key
       },
-      "endpoints" => endpoints
+      "endpoints" => endpoints,
+      "invisible" => User.invisible?(user)
     }
     |> Map.merge(Utils.make_json_ld_header())
   end
@@ -78,8 +79,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
     emoji_tags = Transmogrifier.take_emoji_tags(user)
 
     fields =
-      user.info
-      |> User.Info.fields()
+      user
+      |> User.fields()
       |> Enum.map(fn %{"name" => name, "value" => value} ->
         %{
           "name" => Pleroma.HTML.strip_tags(name),
@@ -99,7 +100,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
       "name" => user.name,
       "summary" => user.bio,
       "url" => user.ap_id,
-      "manuallyApprovesFollowers" => user.info.locked,
+      "manuallyApprovesFollowers" => user.locked,
       "publicKey" => %{
         "id" => "#{user.ap_id}#main-key",
         "owner" => user.ap_id,
@@ -107,8 +108,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
       },
       "endpoints" => endpoints,
       "attachment" => fields,
-      "tag" => (user.info.source_data["tag"] || []) ++ emoji_tags,
-      "discoverable" => user.info.discoverable
+      "tag" => (user.source_data["tag"] || []) ++ emoji_tags,
+      "discoverable" => user.discoverable
     }
     |> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
     |> Map.merge(maybe_make_image(&User.banner_url/2, "image", user))
@@ -116,8 +117,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
   end
 
   def render("following.json", %{user: user, page: page} = opts) do
-    showing_items = (opts[:for] && opts[:for] == user) || !user.info.hide_follows
-    showing_count = showing_items || !user.info.hide_follows_count
+    showing_items = (opts[:for] && opts[:for] == user) || !user.hide_follows
+    showing_count = showing_items || !user.hide_follows_count
 
     query = User.get_friends_query(user)
     query = from(user in query, select: [:ap_id])
@@ -135,8 +136,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
   end
 
   def render("following.json", %{user: user} = opts) do
-    showing_items = (opts[:for] && opts[:for] == user) || !user.info.hide_follows
-    showing_count = showing_items || !user.info.hide_follows_count
+    showing_items = (opts[:for] && opts[:for] == user) || !user.hide_follows
+    showing_count = showing_items || !user.hide_follows_count
 
     query = User.get_friends_query(user)
     query = from(user in query, select: [:ap_id])
@@ -155,7 +156,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
       "totalItems" => total,
       "first" =>
         if showing_items do
-          collection(following, "#{user.ap_id}/following", 1, !user.info.hide_follows)
+          collection(following, "#{user.ap_id}/following", 1, !user.hide_follows)
         else
           "#{user.ap_id}/following?page=1"
         end
@@ -164,8 +165,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
   end
 
   def render("followers.json", %{user: user, page: page} = opts) do
-    showing_items = (opts[:for] && opts[:for] == user) || !user.info.hide_followers
-    showing_count = showing_items || !user.info.hide_followers_count
+    showing_items = (opts[:for] && opts[:for] == user) || !user.hide_followers
+    showing_count = showing_items || !user.hide_followers_count
 
     query = User.get_followers_query(user)
     query = from(user in query, select: [:ap_id])
@@ -183,8 +184,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
   end
 
   def render("followers.json", %{user: user} = opts) do
-    showing_items = (opts[:for] && opts[:for] == user) || !user.info.hide_followers
-    showing_count = showing_items || !user.info.hide_followers_count
+    showing_items = (opts[:for] && opts[:for] == user) || !user.hide_followers
+    showing_count = showing_items || !user.hide_followers_count
 
     query = User.get_followers_query(user)
     query = from(user in query, select: [:ap_id])
index 270d0fa0211f87146408ae5fead8476c2fb5eb9f..f3ab48f7cf8fb1854b1b0aa3c81d0200752c56a5 100644 (file)
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
   alias Pleroma.Object
   alias Pleroma.Repo
   alias Pleroma.User
+  alias Pleroma.Web.ActivityPub.Utils
 
   require Pleroma.Constants
 
@@ -15,7 +16,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
   def is_public?(%Object{data: data}), do: is_public?(data)
   def is_public?(%Activity{data: data}), do: is_public?(data)
   def is_public?(%{"directMessage" => true}), do: false
-  def is_public?(data), do: Pleroma.Constants.as_public() in (data["to"] ++ (data["cc"] || []))
+  def is_public?(data), do: Utils.label_in_message?(Pleroma.Constants.as_public(), data)
 
   def is_private?(activity) do
     with false <- is_public?(activity),
index 513bae80060bc5506a1708bee93b99f357bb6ce6..7ffbb23e7c77d83ce773f5a31120b64d4dc762be 100644 (file)
@@ -46,11 +46,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
            :user_delete,
            :users_create,
            :user_toggle_activation,
+           :user_activate,
+           :user_deactivate,
            :tag_users,
            :untag_users,
            :right_add,
-           :right_delete,
-           :set_activation_status
+           :right_delete
          ]
   )
 
@@ -98,7 +99,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
 
     ModerationLog.insert_log(%{
       actor: admin,
-      subject: user,
+      subject: [user],
       action: "delete"
     })
 
@@ -106,6 +107,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     |> json(nickname)
   end
 
+  def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
+    users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
+    User.delete(users)
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: users,
+      action: "delete"
+    })
+
+    conn
+    |> json(nicknames)
+  end
+
   def user_follow(%{assigns: %{user: admin}} = conn, %{
         "follower" => follower_nick,
         "followed" => followed_nick
@@ -234,13 +249,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
     user = User.get_cached_by_nickname(nickname)
 
-    {:ok, updated_user} = User.deactivate(user, !user.info.deactivated)
+    {:ok, updated_user} = User.deactivate(user, !user.deactivated)
 
-    action = if user.info.deactivated, do: "activate", else: "deactivate"
+    action = if user.deactivated, do: "activate", else: "deactivate"
 
     ModerationLog.insert_log(%{
       actor: admin,
-      subject: user,
+      subject: [user],
       action: action
     })
 
@@ -249,6 +264,36 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     |> render("show.json", %{user: updated_user})
   end
 
+  def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
+    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
+    {:ok, updated_users} = User.deactivate(users, false)
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: users,
+      action: "activate"
+    })
+
+    conn
+    |> put_view(AccountView)
+    |> render("index.json", %{users: Keyword.values(updated_users)})
+  end
+
+  def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
+    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
+    {:ok, updated_users} = User.deactivate(users, true)
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: users,
+      action: "deactivate"
+    })
+
+    conn
+    |> put_view(AccountView)
+    |> render("index.json", %{users: Keyword.values(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(%{
@@ -313,26 +358,51 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     |> Enum.into(%{}, &{&1, true})
   end
 
+  def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
+        "permission_group" => permission_group,
+        "nicknames" => nicknames
+      })
+      when permission_group in ["moderator", "admin"] do
+    update = %{:"is_#{permission_group}" => true}
+
+    users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
+
+    for u <- users, do: User.admin_api_update(u, update)
+
+    ModerationLog.insert_log(%{
+      action: "grant",
+      actor: admin,
+      subject: users,
+      permission: permission_group
+    })
+
+    json(conn, update)
+  end
+
+  def right_add_multiple(conn, _) do
+    render_error(conn, :not_found, "No such permission_group")
+  end
+
   def right_add(%{assigns: %{user: admin}} = conn, %{
         "permission_group" => permission_group,
         "nickname" => nickname
       })
       when permission_group in ["moderator", "admin"] do
-    info = Map.put(%{}, "is_" <> permission_group, true)
+    fields = %{:"is_#{permission_group}" => true}
 
     {:ok, user} =
       nickname
       |> User.get_cached_by_nickname()
-      |> User.update_info(&User.Info.admin_api_update(&1, info))
+      |> User.admin_api_update(fields)
 
     ModerationLog.insert_log(%{
       action: "grant",
       actor: admin,
-      subject: user,
+      subject: [user],
       permission: permission_group
     })
 
-    json(conn, info)
+    json(conn, fields)
   end
 
   def right_add(conn, _) do
@@ -344,13 +414,41 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
 
     conn
     |> json(%{
-      is_moderator: user.info.is_moderator,
-      is_admin: user.info.is_admin
+      is_moderator: user.is_moderator,
+      is_admin: user.is_admin
     })
   end
 
-  def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do
-    render_error(conn, :forbidden, "You can't revoke your own admin status.")
+  def right_delete_multiple(
+        %{assigns: %{user: %{nickname: admin_nickname} = admin}} = conn,
+        %{
+          "permission_group" => permission_group,
+          "nicknames" => nicknames
+        }
+      )
+      when permission_group in ["moderator", "admin"] do
+    with false <- Enum.member?(nicknames, admin_nickname) do
+      update = %{:"is_#{permission_group}" => false}
+
+      users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
+
+      for u <- users, do: User.admin_api_update(u, update)
+
+      ModerationLog.insert_log(%{
+        action: "revoke",
+        actor: admin,
+        subject: users,
+        permission: permission_group
+      })
+
+      json(conn, update)
+    else
+      _ -> render_error(conn, :forbidden, "You can't revoke your own admin/moderator status.")
+    end
+  end
+
+  def right_delete_multiple(conn, _) do
+    render_error(conn, :not_found, "No such permission_group")
   end
 
   def right_delete(
@@ -361,43 +459,34 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
         }
       )
       when permission_group in ["moderator", "admin"] do
-    info = Map.put(%{}, "is_" <> permission_group, false)
+    fields = %{:"is_#{permission_group}" => false}
 
     {:ok, user} =
       nickname
       |> User.get_cached_by_nickname()
-      |> User.update_info(&User.Info.admin_api_update(&1, info))
+      |> User.admin_api_update(fields)
 
     ModerationLog.insert_log(%{
       action: "revoke",
       actor: admin,
-      subject: user,
+      subject: [user],
       permission: permission_group
     })
 
-    json(conn, info)
+    json(conn, fields)
   end
 
-  def right_delete(conn, _) do
-    render_error(conn, :not_found, "No such permission_group")
+  def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do
+    render_error(conn, :forbidden, "You can't revoke your own admin status.")
   end
 
-  def set_activation_status(%{assigns: %{user: admin}} = conn, %{
-        "nickname" => nickname,
-        "status" => status
-      }) do
-    with {:ok, status} <- Ecto.Type.cast(:boolean, status),
-         %User{} = user <- User.get_cached_by_nickname(nickname),
-         {:ok, _} <- User.deactivate(user, !status) do
-      action = if(user.info.deactivated, do: "activate", else: "deactivate")
-
-      ModerationLog.insert_log(%{
-        actor: admin,
-        subject: user,
-        action: action
-      })
-
-      json_response(conn, :no_content, "")
+  def relay_list(conn, _params) do
+    with {:ok, list} <- Relay.list() do
+      json(conn, %{relays: list})
+    else
+      _ ->
+        conn
+        |> put_status(500)
     end
   end
 
index a96affd40cbe4800b0b213c287144688f505ac62..6aa7257cead895b166043f343f10f26d278fbb9a 100644 (file)
@@ -7,7 +7,6 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
 
   alias Pleroma.HTML
   alias Pleroma.User
-  alias Pleroma.User.Info
   alias Pleroma.Web.AdminAPI.AccountView
   alias Pleroma.Web.MediaProxy
 
@@ -19,6 +18,12 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
     }
   end
 
+  def render("index.json", %{users: users}) do
+    %{
+      users: render_many(users, AccountView, "show.json", as: :user)
+    }
+  end
+
   def render("show.json", %{user: user}) do
     avatar = User.avatar_url(user) |> MediaProxy.url()
     display_name = HTML.strip_tags(user.name || user.nickname)
@@ -28,9 +33,9 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
       "avatar" => avatar,
       "nickname" => user.nickname,
       "display_name" => display_name,
-      "deactivated" => user.info.deactivated,
+      "deactivated" => user.deactivated,
       "local" => user.local,
-      "roles" => Info.roles(user.info),
+      "roles" => User.roles(user),
       "tags" => user.tags || []
     }
   end
index 386408d51f24a7127be51caee97d319f1a5673bb..449b808b58c9a685c7e7591cc53669e888df4b08 100644 (file)
@@ -263,10 +263,10 @@ defmodule Pleroma.Web.CommonAPI do
   # Updates the emojis for a user based on their profile
   def update(user) do
     emoji = emoji_from_profile(user)
-    source_data = user.info |> Map.get(:source_data, %{}) |> Map.put("tag", emoji)
+    source_data = Map.put(user.source_data, "tag", emoji)
 
     user =
-      case User.update_info(user, &User.Info.set_source_data(&1, source_data)) do
+      case User.update_source_data(user, source_data) do
         {:ok, user} -> user
         _ -> user
       end
@@ -287,20 +287,20 @@ defmodule Pleroma.Web.CommonAPI do
            object: %Object{data: %{"type" => "Note"}}
          } = activity <- get_by_id_or_ap_id(id_or_ap_id),
          true <- Visibility.is_public?(activity),
-         {:ok, _user} <- User.update_info(user, &User.Info.add_pinnned_activity(&1, activity)) do
+         {:ok, _user} <- User.add_pinnned_activity(user, activity) do
       {:ok, activity}
     else
-      {:error, %{changes: %{info: %{errors: [pinned_activities: {err, _}]}}}} -> {:error, err}
+      {:error, %{errors: [pinned_activities: {err, _}]}} -> {:error, err}
       _ -> {:error, dgettext("errors", "Could not pin")}
     end
   end
 
   def unpin(id_or_ap_id, user) do
     with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
-         {:ok, _user} <- User.update_info(user, &User.Info.remove_pinnned_activity(&1, activity)) do
+         {:ok, _user} <- User.remove_pinnned_activity(user, activity) do
       {:ok, activity}
     else
-      %{errors: [pinned_activities: {err, _}]} -> {:error, err}
+      {:error, %{errors: [pinned_activities: {err, _}]}} -> {:error, err}
       _ -> {:error, dgettext("errors", "Could not unpin")}
     end
   end
@@ -392,14 +392,14 @@ defmodule Pleroma.Web.CommonAPI do
   defp set_visibility(activity, _), do: {:ok, activity}
 
   def hide_reblogs(user, %{ap_id: ap_id} = _muted) do
-    if ap_id not in user.info.muted_reblogs do
-      User.update_info(user, &User.Info.add_reblog_mute(&1, ap_id))
+    if ap_id not in user.muted_reblogs do
+      User.add_reblog_mute(user, ap_id)
     end
   end
 
   def show_reblogs(user, %{ap_id: ap_id} = _muted) do
-    if ap_id in user.info.muted_reblogs do
-      User.update_info(user, &User.Info.remove_reblog_mute(&1, ap_id))
+    if ap_id in user.muted_reblogs do
+      User.remove_reblog_mute(user, ap_id)
     end
   end
 end
index 1a2da014ae054999325a55eaf1c844a12d1210e4..e8a56ebd706d7f243328c7cad05202bcf1b5a626 100644 (file)
@@ -10,19 +10,11 @@ defmodule Pleroma.Web.Federator do
   alias Pleroma.Web.ActivityPub.Transmogrifier
   alias Pleroma.Web.ActivityPub.Utils
   alias Pleroma.Web.Federator.Publisher
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.Websub
   alias Pleroma.Workers.PublisherWorker
   alias Pleroma.Workers.ReceiverWorker
-  alias Pleroma.Workers.SubscriberWorker
 
   require Logger
 
-  def init do
-    # To do: consider removing this call in favor of scheduled execution (`quantum`-based)
-    refresh_subscriptions(schedule_in: 60)
-  end
-
   @doc "Addresses [memory leaks on recursive replies fetching](https://git.pleroma.social/pleroma/pleroma/issues/161)"
   # credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
   def allowed_incoming_reply_depth?(depth) do
@@ -37,10 +29,6 @@ defmodule Pleroma.Web.Federator do
 
   # Client API
 
-  def incoming_doc(doc) do
-    ReceiverWorker.enqueue("incoming_doc", %{"body" => doc})
-  end
-
   def incoming_ap_doc(params) do
     ReceiverWorker.enqueue("incoming_ap_doc", %{"params" => params})
   end
@@ -53,18 +41,6 @@ defmodule Pleroma.Web.Federator do
     PublisherWorker.enqueue("publish", %{"activity_id" => activity.id})
   end
 
-  def verify_websub(websub) do
-    SubscriberWorker.enqueue("verify_websub", %{"websub_id" => websub.id})
-  end
-
-  def request_subscription(websub) do
-    SubscriberWorker.enqueue("request_subscription", %{"websub_id" => websub.id})
-  end
-
-  def refresh_subscriptions(worker_args \\ []) do
-    SubscriberWorker.enqueue("refresh_subscriptions", %{}, worker_args ++ [max_attempts: 1])
-  end
-
   # Job Worker Callbacks
 
   @spec perform(atom(), module(), any()) :: {:ok, any()} | {:error, any()}
@@ -81,11 +57,6 @@ defmodule Pleroma.Web.Federator do
     end
   end
 
-  def perform(:incoming_doc, doc) do
-    Logger.info("Got document, trying to parse")
-    OStatus.handle_incoming(doc)
-  end
-
   def perform(:incoming_ap_doc, params) do
     Logger.info("Handling incoming AP activity")
 
@@ -111,29 +82,6 @@ defmodule Pleroma.Web.Federator do
     end
   end
 
-  def perform(:request_subscription, websub) do
-    Logger.debug("Refreshing #{websub.topic}")
-
-    with {:ok, websub} <- Websub.request_subscription(websub) do
-      Logger.debug("Successfully refreshed #{websub.topic}")
-    else
-      _e -> Logger.debug("Couldn't refresh #{websub.topic}")
-    end
-  end
-
-  def perform(:verify_websub, websub) do
-    Logger.debug(fn ->
-      "Running WebSub verification for #{websub.id} (#{websub.topic}, #{websub.callback})"
-    end)
-
-    Websub.verify(websub)
-  end
-
-  def perform(:refresh_subscriptions) do
-    Logger.debug("Federator running refresh subscriptions")
-    Websub.refresh_subscriptions()
-  end
-
   def ap_enabled_actor(id) do
     user = User.get_cached_by_ap_id(id)
 
index 937064638fc72ee9d8d1b21b80ac34656e17fc89..fb9b26649b5a3a0dff1b3c60907fb244a970ac71 100644 (file)
@@ -80,4 +80,30 @@ defmodule Pleroma.Web.Federator.Publisher do
       links ++ module.gather_nodeinfo_protocol_names()
     end)
   end
+
+  @doc """
+  Gathers a set of remote users given an IR envelope.
+  """
+  def remote_users(%User{id: user_id}, %{data: %{"to" => to} = data}) do
+    cc = Map.get(data, "cc", [])
+
+    bcc =
+      data
+      |> Map.get("bcc", [])
+      |> Enum.reduce([], fn ap_id, bcc ->
+        case Pleroma.List.get_by_ap_id(ap_id) do
+          %Pleroma.List{user_id: ^user_id} = list ->
+            {:ok, following} = Pleroma.List.get_following(list)
+            bcc ++ Enum.map(following, & &1.ap_id)
+
+          _ ->
+            bcc
+        end
+      end)
+
+    [to, cc, bcc]
+    |> Enum.concat()
+    |> Enum.map(&User.get_cached_by_ap_id/1)
+    |> Enum.filter(fn user -> user && !user.local end)
+  end
 end
index 87860f1d5a2a5c0979cc68801ca4357d244e8379..ca261ad6ed96a944822cc130e8bb6769a37ee407 100644 (file)
@@ -34,9 +34,15 @@ defmodule Pleroma.Web.MastoFEController do
     end
   end
 
+  @doc "GET /web/manifest.json"
+  def manifest(conn, _params) do
+    conn
+    |> render("manifest.json")
+  end
+
   @doc "PUT /api/web/settings"
   def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
-    with {:ok, _} <- User.update_info(user, &User.Info.mastodon_settings_update(&1, settings)) do
+    with {:ok, _} <- User.mastodon_settings_update(user, settings) do
       json(conn, %{})
     else
       e ->
index 9ef7fd48ddda0f4079fc4cac8a22b09197df48aa..73fad519ecbe1259f3ab0782beaddbf818943bcc 100644 (file)
@@ -130,25 +130,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
   def update_credentials(%{assigns: %{user: original_user}} = conn, params) do
     user = original_user
 
-    user_params =
-      %{}
-      |> add_if_present(params, "display_name", :name)
-      |> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value, user)} end)
-      |> add_if_present(params, "avatar", :avatar, fn value ->
-        with %Plug.Upload{} <- value,
-             {:ok, object} <- ActivityPub.upload(value, type: :avatar) do
-          {:ok, object.data}
-        end
-      end)
-
-    emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "")
-
-    user_info_emojis =
-      user.info
-      |> Map.get(:emoji, [])
-      |> Enum.concat(Emoji.Formatter.get_emoji_map(emojis_text))
-      |> Enum.dedup()
-
     params =
       if Map.has_key?(params, "fields_attributes") do
         Map.update!(params, "fields_attributes", fn fields ->
@@ -160,7 +141,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
         params
       end
 
-    info_params =
+    user_params =
       [
         :no_rich_text,
         :locked,
@@ -176,15 +157,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
       |> Enum.reduce(%{}, fn key, acc ->
         add_if_present(acc, params, to_string(key), key, &{:ok, truthy_param?(&1)})
       end)
-      |> add_if_present(params, "default_scope", :default_scope)
-      |> add_if_present(params, "fields_attributes", :fields, fn fields ->
-        fields = Enum.map(fields, fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end)
-
-        {:ok, fields}
-      end)
-      |> add_if_present(params, "fields_attributes", :raw_fields)
-      |> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store, fn value ->
-        {:ok, Map.merge(user.info.pleroma_settings_store, value)}
+      |> add_if_present(params, "display_name", :name)
+      |> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value, user)} end)
+      |> add_if_present(params, "avatar", :avatar, fn value ->
+        with %Plug.Upload{} <- value,
+             {:ok, object} <- ActivityPub.upload(value, type: :avatar) do
+          {:ok, object.data}
+        end
       end)
       |> add_if_present(params, "header", :banner, fn value ->
         with %Plug.Upload{} <- value,
@@ -198,12 +177,27 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
           {:ok, object.data}
         end
       end)
-      |> Map.put(:emoji, user_info_emojis)
+      |> add_if_present(params, "fields_attributes", :fields, fn fields ->
+        fields = Enum.map(fields, fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end)
 
-    changeset =
+        {:ok, fields}
+      end)
+      |> add_if_present(params, "fields_attributes", :raw_fields)
+      |> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store, fn value ->
+        {:ok, Map.merge(user.pleroma_settings_store, value)}
+      end)
+      |> add_if_present(params, "default_scope", :default_scope)
+
+    emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "")
+
+    user_emojis =
       user
-      |> User.update_changeset(user_params)
-      |> User.change_info(&User.Info.profile_update(&1, info_params))
+      |> Map.get(:emoji, [])
+      |> Enum.concat(Emoji.Formatter.get_emoji_map(emojis_text))
+      |> Enum.dedup()
+
+    user_params = Map.put(user_params, :emoji, user_emojis)
+    changeset = User.update_changeset(user, user_params)
 
     with {:ok, user} <- User.update_and_set_cache(changeset) do
       if original_user != user, do: CommonAPI.update(user)
@@ -269,7 +263,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
     followers =
       cond do
         for_user && user.id == for_user.id -> MastodonAPI.get_followers(user, params)
-        user.info.hide_followers -> []
+        user.hide_followers -> []
         true -> MastodonAPI.get_followers(user, params)
       end
 
@@ -283,7 +277,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
     followers =
       cond do
         for_user && user.id == for_user.id -> MastodonAPI.get_friends(user, params)
-        user.info.hide_follows -> []
+        user.hide_follows -> []
         true -> MastodonAPI.get_friends(user, params)
       end
 
index c7606246b62f2f32d4720e5fa8ff11f747050d28..456fe7ab22b48fe17fd3de93b39ddfb0766d37a6 100644 (file)
@@ -21,8 +21,8 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do
   plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
 
   @doc "GET /api/v1/domain_blocks"
-  def index(%{assigns: %{user: %{info: info}}} = conn, _) do
-    json(conn, Map.get(info, :domain_blocks, []))
+  def index(%{assigns: %{user: user}} = conn, _) do
+    json(conn, Map.get(user, :domain_blocks, []))
   end
 
   @doc "POST /api/v1/domain_blocks"
diff --git a/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex b/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex
new file mode 100644 (file)
index 0000000..ce02562
--- /dev/null
@@ -0,0 +1,32 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.MarkerController do
+  use Pleroma.Web, :controller
+  alias Pleroma.Plugs.OAuthScopesPlug
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["read:statuses"]}
+    when action == :index
+  )
+
+  plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :upsert)
+  plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
+  action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
+
+  # GET /api/v1/markers
+  def index(%{assigns: %{user: user}} = conn, params) do
+    markers = Pleroma.Marker.get_markers(user, params["timeline"])
+    render(conn, "markers.json", %{markers: markers})
+  end
+
+  # POST /api/v1/markers
+  def upsert(%{assigns: %{user: user}} = conn, params) do
+    with {:ok, result} <- Pleroma.Marker.upsert(user, params),
+         markers <- Map.values(result) do
+      render(conn, "markers.json", %{markers: markers})
+    end
+  end
+end
index 2d497689115c1cd85d990b95a9b00c0708464fe4..e30fed6102b7b16a491cfc0f6010df1570371025 100644 (file)
@@ -74,23 +74,23 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
     user_info = User.get_cached_user_info(user)
 
     following_count =
-      if !user.info.hide_follows_count or !user.info.hide_follows or opts[:for] == user do
+      if !user.hide_follows_count or !user.hide_follows or opts[:for] == user do
         user_info.following_count
       else
         0
       end
 
     followers_count =
-      if !user.info.hide_followers_count or !user.info.hide_followers or opts[:for] == user do
+      if !user.hide_followers_count or !user.hide_followers or opts[:for] == user do
         user_info.follower_count
       else
         0
       end
 
-    bot = (user.info.source_data["type"] || "Person") in ["Application", "Service"]
+    bot = (user.source_data["type"] || "Person") in ["Application", "Service"]
 
     emojis =
-      (user.info.source_data["tag"] || [])
+      (user.source_data["tag"] || [])
       |> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
       |> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} ->
         %{
@@ -102,8 +102,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
       end)
 
     fields =
-      user.info
-      |> User.Info.fields()
+      user
+      |> User.fields()
       |> Enum.map(fn %{"name" => name, "value" => value} ->
         %{
           "name" => Pleroma.HTML.strip_tags(name),
@@ -111,23 +111,19 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
         }
       end)
 
-    raw_fields = Map.get(user.info, :raw_fields, [])
-
     bio = HTML.filter_tags(user.bio, User.html_filter_policy(opts[:for]))
     relationship = render("relationship.json", %{user: opts[:for], target: user})
 
-    discoverable = user.info.discoverable
-
     %{
       id: to_string(user.id),
       username: username_from_nickname(user.nickname),
       acct: user.nickname,
       display_name: display_name,
-      locked: user_info.locked,
+      locked: user.locked,
       created_at: Utils.to_masto_date(user.inserted_at),
       followers_count: followers_count,
       following_count: following_count,
-      statuses_count: user_info.note_count,
+      statuses_count: user.note_count,
       note: bio || "",
       url: User.profile_url(user),
       avatar: image,
@@ -140,9 +136,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
       source: %{
         note: HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
         sensitive: false,
-        fields: raw_fields,
+        fields: user.raw_fields,
         pleroma: %{
-          discoverable: discoverable
+          discoverable: user.discoverable
         }
       },
 
@@ -150,14 +146,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
       pleroma: %{
         confirmation_pending: user_info.confirmation_pending,
         tags: user.tags,
-        hide_followers_count: user.info.hide_followers_count,
-        hide_follows_count: user.info.hide_follows_count,
-        hide_followers: user.info.hide_followers,
-        hide_follows: user.info.hide_follows,
-        hide_favorites: user.info.hide_favorites,
+        hide_followers_count: user.hide_followers_count,
+        hide_follows_count: user.hide_follows_count,
+        hide_followers: user.hide_followers,
+        hide_follows: user.hide_follows,
+        hide_favorites: user.hide_favorites,
         relationship: relationship,
-        skip_thread_containment: user.info.skip_thread_containment,
-        background_image: image_url(user.info.background) |> MediaProxy.url()
+        skip_thread_containment: user.skip_thread_containment,
+        background_image: image_url(user.background) |> MediaProxy.url()
       }
     }
     |> maybe_put_role(user, opts[:for])
@@ -195,21 +191,21 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
          data,
          %User{id: user_id} = user,
          %User{id: user_id},
-         user_info
+         _user_info
        ) do
     data
-    |> Kernel.put_in([:source, :privacy], user_info.default_scope)
-    |> Kernel.put_in([:source, :pleroma, :show_role], user.info.show_role)
-    |> Kernel.put_in([:source, :pleroma, :no_rich_text], user.info.no_rich_text)
+    |> Kernel.put_in([:source, :privacy], user.default_scope)
+    |> Kernel.put_in([:source, :pleroma, :show_role], user.show_role)
+    |> Kernel.put_in([:source, :pleroma, :no_rich_text], user.no_rich_text)
   end
 
   defp maybe_put_settings(data, _, _, _), do: data
 
-  defp maybe_put_settings_store(data, %User{info: info, id: id}, %User{id: id}, %{
+  defp maybe_put_settings_store(data, %User{} = user, %User{}, %{
          with_pleroma_settings: true
        }) do
     data
-    |> Kernel.put_in([:pleroma, :settings_store], info.pleroma_settings_store)
+    |> Kernel.put_in([:pleroma, :settings_store], user.pleroma_settings_store)
   end
 
   defp maybe_put_settings_store(data, _, _, _), do: data
@@ -223,28 +219,28 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
 
   defp maybe_put_chat_token(data, _, _, _), do: data
 
-  defp maybe_put_role(data, %User{info: %{show_role: true}} = user, _) do
+  defp maybe_put_role(data, %User{show_role: true} = user, _) do
     data
-    |> Kernel.put_in([:pleroma, :is_admin], user.info.is_admin)
-    |> Kernel.put_in([:pleroma, :is_moderator], user.info.is_moderator)
+    |> Kernel.put_in([:pleroma, :is_admin], user.is_admin)
+    |> Kernel.put_in([:pleroma, :is_moderator], user.is_moderator)
   end
 
   defp maybe_put_role(data, %User{id: user_id} = user, %User{id: user_id}) do
     data
-    |> Kernel.put_in([:pleroma, :is_admin], user.info.is_admin)
-    |> Kernel.put_in([:pleroma, :is_moderator], user.info.is_moderator)
+    |> Kernel.put_in([:pleroma, :is_admin], user.is_admin)
+    |> Kernel.put_in([:pleroma, :is_moderator], user.is_moderator)
   end
 
   defp maybe_put_role(data, _, _), do: data
 
   defp maybe_put_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do
-    Kernel.put_in(data, [:pleroma, :notification_settings], user.info.notification_settings)
+    Kernel.put_in(data, [:pleroma, :notification_settings], user.notification_settings)
   end
 
   defp maybe_put_notification_settings(data, _, _), do: data
 
-  defp maybe_put_activation_status(data, user, %User{info: %{is_admin: true}}) do
-    Kernel.put_in(data, [:pleroma, :deactivated], user.info.deactivated)
+  defp maybe_put_activation_status(data, user, %User{is_admin: true}) do
+    Kernel.put_in(data, [:pleroma, :deactivated], user.deactivated)
   end
 
   defp maybe_put_activation_status(data, _, _), do: data
@@ -253,7 +249,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
     data
     |> Kernel.put_in(
       [:pleroma, :unread_conversation_count],
-      user.info.unread_conversation_count
+      user.unread_conversation_count
     )
   end
 
diff --git a/lib/pleroma/web/mastodon_api/views/marker_view.ex b/lib/pleroma/web/mastodon_api/views/marker_view.ex
new file mode 100644 (file)
index 0000000..38fbeed
--- /dev/null
@@ -0,0 +1,17 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.MarkerView do
+  use Pleroma.Web, :view
+
+  def render("markers.json", %{markers: markers}) do
+    Enum.reduce(markers, %{}, fn m, acc ->
+      Map.put_new(acc, m.timeline, %{
+        last_read_id: m.last_read_id,
+        version: m.lock_version,
+        updated_at: NaiveDateTime.to_iso8601(m.updated_at)
+      })
+    end)
+  end
+end
index 9b8dd30867abfb237dc21cc1bcfcc39574824f8a..b785ca9d47d60854ba51863b11a87428dec1740c 100644 (file)
@@ -498,6 +498,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
   defp present?(false), do: false
   defp present?(_), do: true
 
-  defp pinned?(%Activity{id: id}, %User{info: %{pinned_activities: pinned_activities}}),
+  defp pinned?(%Activity{id: id}, %User{pinned_activities: pinned_activities}),
     do: id in pinned_activities
 end
index 3c26eb4069b2a2fb7bad3986d680ee0ffe21fd01..a400d1c8d7e54186038aeb2528a32bbbec6ec609 100644 (file)
@@ -35,6 +35,13 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
          {_, stream} <- List.keyfind(params, "stream", 0),
          {:ok, user} <- allow_request(stream, [access_token, sec_websocket]),
          topic when is_binary(topic) <- expand_topic(stream, params) do
+      req =
+        if sec_websocket do
+          :cowboy_req.set_resp_header("sec-websocket-protocol", sec_websocket, req)
+        else
+          req
+        end
+
       {:cowboy_websocket, req, %{user: user, topic: topic}, %{idle_timeout: @timeout}}
     else
       {:error, code} ->
index 03c9a502786d279b73e2d3b6be0164f91fad58de..fe71aca8cea95f26f5523a439674ce620aa30cbd 100644 (file)
@@ -202,9 +202,9 @@ defmodule Pleroma.Web.OAuth.OAuthController do
     with {:ok, %User{} = user} <- Authenticator.get_user(conn),
          {:ok, app} <- Token.Utils.fetch_app(conn),
          {:auth_active, true} <- {:auth_active, User.auth_active?(user)},
-         {:user_active, true} <- {:user_active, !user.info.deactivated},
+         {:user_active, true} <- {:user_active, !user.deactivated},
          {:password_reset_pending, false} <-
-           {:password_reset_pending, user.info.password_reset_pending},
+           {:password_reset_pending, user.password_reset_pending},
          {:ok, scopes} <- validate_scopes(app, params),
          {:ok, auth} <- Authorization.create_authorization(app, user, scopes),
          {:ok, token} <- Token.exchange_token(app, auth) do
diff --git a/lib/pleroma/web/ostatus/activity_representer.ex b/lib/pleroma/web/ostatus/activity_representer.ex
deleted file mode 100644 (file)
index 8e55b9f..0000000
+++ /dev/null
@@ -1,313 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.ActivityRepresenter do
-  alias Pleroma.Activity
-  alias Pleroma.Object
-  alias Pleroma.User
-  alias Pleroma.Web.OStatus.UserRepresenter
-
-  require Logger
-  require Pleroma.Constants
-
-  defp get_href(id) do
-    with %Object{data: %{"external_url" => external_url}} <- Object.get_cached_by_ap_id(id) do
-      external_url
-    else
-      _e -> id
-    end
-  end
-
-  defp get_in_reply_to(activity) do
-    with %Object{data: %{"inReplyTo" => in_reply_to}} <- Object.normalize(activity) do
-      [
-        {:"thr:in-reply-to",
-         [ref: to_charlist(in_reply_to), href: to_charlist(get_href(in_reply_to))], []}
-      ]
-    else
-      _ ->
-        []
-    end
-  end
-
-  defp get_mentions(to) do
-    Enum.map(to, fn id ->
-      cond do
-        # Special handling for the AP/Ostatus public collections
-        Pleroma.Constants.as_public() == id ->
-          {:link,
-           [
-             rel: "mentioned",
-             "ostatus:object-type": "http://activitystrea.ms/schema/1.0/collection",
-             href: "http://activityschema.org/collection/public"
-           ], []}
-
-        # Ostatus doesn't handle follower collections, ignore these.
-        Regex.match?(~r/^#{Pleroma.Web.base_url()}.+followers$/, id) ->
-          []
-
-        true ->
-          {:link,
-           [
-             rel: "mentioned",
-             "ostatus:object-type": "http://activitystrea.ms/schema/1.0/person",
-             href: id
-           ], []}
-      end
-    end)
-  end
-
-  defp get_links(%{local: true}, %{"id" => object_id}) do
-    h = fn str -> [to_charlist(str)] end
-
-    [
-      {:link, [type: ['application/atom+xml'], href: h.(object_id), rel: 'self'], []},
-      {:link, [type: ['text/html'], href: h.(object_id), rel: 'alternate'], []}
-    ]
-  end
-
-  defp get_links(%{local: false}, %{"external_url" => external_url}) do
-    h = fn str -> [to_charlist(str)] end
-
-    [
-      {:link, [type: ['text/html'], href: h.(external_url), rel: 'alternate'], []}
-    ]
-  end
-
-  defp get_links(_activity, _object_data), do: []
-
-  defp get_emoji_links(emojis) do
-    Enum.map(emojis, fn {emoji, file} ->
-      {:link, [name: to_charlist(emoji), rel: 'emoji', href: to_charlist(file)], []}
-    end)
-  end
-
-  def to_simple_form(activity, user, with_author \\ false)
-
-  def to_simple_form(%{data: %{"type" => "Create"}} = activity, user, with_author) do
-    h = fn str -> [to_charlist(str)] end
-
-    object = Object.normalize(activity)
-
-    updated_at = object.data["published"]
-    inserted_at = object.data["published"]
-
-    attachments =
-      Enum.map(object.data["attachment"] || [], fn attachment ->
-        url = hd(attachment["url"])
-
-        {:link,
-         [rel: 'enclosure', href: to_charlist(url["href"]), type: to_charlist(url["mediaType"])],
-         []}
-      end)
-
-    in_reply_to = get_in_reply_to(activity)
-    author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
-    mentions = activity.recipients |> get_mentions
-
-    categories =
-      (object.data["tag"] || [])
-      |> Enum.map(fn tag ->
-        if is_binary(tag) do
-          {:category, [term: to_charlist(tag)], []}
-        else
-          nil
-        end
-      end)
-      |> Enum.filter(& &1)
-
-    emoji_links = get_emoji_links(object.data["emoji"] || %{})
-
-    summary =
-      if object.data["summary"] do
-        [{:summary, [], h.(object.data["summary"])}]
-      else
-        []
-      end
-
-    [
-      {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/note']},
-      {:"activity:verb", ['http://activitystrea.ms/schema/1.0/post']},
-      # For notes, federate the object id.
-      {:id, h.(object.data["id"])},
-      {:title, ['New note by #{user.nickname}']},
-      {:content, [type: 'html'], h.(object.data["content"] |> String.replace(~r/[\n\r]/, ""))},
-      {:published, h.(inserted_at)},
-      {:updated, h.(updated_at)},
-      {:"ostatus:conversation", [ref: h.(activity.data["context"])],
-       h.(activity.data["context"])},
-      {:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []}
-    ] ++
-      summary ++
-      get_links(activity, object.data) ++
-      categories ++ attachments ++ in_reply_to ++ author ++ mentions ++ emoji_links
-  end
-
-  def to_simple_form(%{data: %{"type" => "Like"}} = activity, user, with_author) do
-    h = fn str -> [to_charlist(str)] end
-
-    updated_at = activity.data["published"]
-    inserted_at = activity.data["published"]
-
-    author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
-    mentions = activity.recipients |> get_mentions
-
-    [
-      {:"activity:verb", ['http://activitystrea.ms/schema/1.0/favorite']},
-      {:id, h.(activity.data["id"])},
-      {:title, ['New favorite by #{user.nickname}']},
-      {:content, [type: 'html'], ['#{user.nickname} favorited something']},
-      {:published, h.(inserted_at)},
-      {:updated, h.(updated_at)},
-      {:"activity:object",
-       [
-         {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/note']},
-         # For notes, federate the object id.
-         {:id, h.(activity.data["object"])}
-       ]},
-      {:"ostatus:conversation", [ref: h.(activity.data["context"])],
-       h.(activity.data["context"])},
-      {:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []},
-      {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []},
-      {:"thr:in-reply-to", [ref: to_charlist(activity.data["object"])], []}
-    ] ++ author ++ mentions
-  end
-
-  def to_simple_form(%{data: %{"type" => "Announce"}} = activity, user, with_author) do
-    h = fn str -> [to_charlist(str)] end
-
-    updated_at = activity.data["published"]
-    inserted_at = activity.data["published"]
-
-    author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
-
-    retweeted_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
-    retweeted_object = Object.normalize(retweeted_activity)
-    retweeted_user = User.get_cached_by_ap_id(retweeted_activity.data["actor"])
-
-    retweeted_xml = to_simple_form(retweeted_activity, retweeted_user, true)
-
-    mentions =
-      ([retweeted_user.ap_id] ++ activity.recipients)
-      |> Enum.uniq()
-      |> get_mentions()
-
-    [
-      {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
-      {:"activity:verb", ['http://activitystrea.ms/schema/1.0/share']},
-      {:id, h.(activity.data["id"])},
-      {:title, ['#{user.nickname} repeated a notice']},
-      {:content, [type: 'html'], ['RT #{retweeted_object.data["content"]}']},
-      {:published, h.(inserted_at)},
-      {:updated, h.(updated_at)},
-      {:"ostatus:conversation", [ref: h.(activity.data["context"])],
-       h.(activity.data["context"])},
-      {:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []},
-      {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []},
-      {:"activity:object", retweeted_xml}
-    ] ++ mentions ++ author
-  end
-
-  def to_simple_form(%{data: %{"type" => "Follow"}} = activity, user, with_author) do
-    h = fn str -> [to_charlist(str)] end
-
-    updated_at = activity.data["published"]
-    inserted_at = activity.data["published"]
-
-    author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
-
-    mentions = (activity.recipients || []) |> get_mentions
-
-    [
-      {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
-      {:"activity:verb", ['http://activitystrea.ms/schema/1.0/follow']},
-      {:id, h.(activity.data["id"])},
-      {:title, ['#{user.nickname} started following #{activity.data["object"]}']},
-      {:content, [type: 'html'],
-       ['#{user.nickname} started following #{activity.data["object"]}']},
-      {:published, h.(inserted_at)},
-      {:updated, h.(updated_at)},
-      {:"activity:object",
-       [
-         {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']},
-         {:id, h.(activity.data["object"])},
-         {:uri, h.(activity.data["object"])}
-       ]},
-      {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}
-    ] ++ mentions ++ author
-  end
-
-  # Only undos of follow for now. Will need to get redone once there are more
-  def to_simple_form(
-        %{data: %{"type" => "Undo", "object" => %{"type" => "Follow"} = follow_activity}} =
-          activity,
-        user,
-        with_author
-      ) do
-    h = fn str -> [to_charlist(str)] end
-
-    updated_at = activity.data["published"]
-    inserted_at = activity.data["published"]
-
-    author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
-
-    mentions = (activity.recipients || []) |> get_mentions
-    follow_activity = Activity.normalize(follow_activity)
-
-    [
-      {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
-      {:"activity:verb", ['http://activitystrea.ms/schema/1.0/unfollow']},
-      {:id, h.(activity.data["id"])},
-      {:title, ['#{user.nickname} stopped following #{follow_activity.data["object"]}']},
-      {:content, [type: 'html'],
-       ['#{user.nickname} stopped following #{follow_activity.data["object"]}']},
-      {:published, h.(inserted_at)},
-      {:updated, h.(updated_at)},
-      {:"activity:object",
-       [
-         {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']},
-         {:id, h.(follow_activity.data["object"])},
-         {:uri, h.(follow_activity.data["object"])}
-       ]},
-      {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}
-    ] ++ mentions ++ author
-  end
-
-  def to_simple_form(%{data: %{"type" => "Delete"}} = activity, user, with_author) do
-    h = fn str -> [to_charlist(str)] end
-
-    updated_at = activity.data["published"]
-    inserted_at = activity.data["published"]
-
-    author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
-
-    [
-      {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
-      {:"activity:verb", ['http://activitystrea.ms/schema/1.0/delete']},
-      {:id, h.(activity.data["object"])},
-      {:title, ['An object was deleted']},
-      {:content, [type: 'html'], ['An object was deleted']},
-      {:published, h.(inserted_at)},
-      {:updated, h.(updated_at)}
-    ] ++ author
-  end
-
-  def to_simple_form(_, _, _), do: nil
-
-  def wrap_with_entry(simple_form) do
-    [
-      {
-        :entry,
-        [
-          xmlns: 'http://www.w3.org/2005/Atom',
-          "xmlns:thr": 'http://purl.org/syndication/thread/1.0',
-          "xmlns:activity": 'http://activitystrea.ms/spec/1.0/',
-          "xmlns:poco": 'http://portablecontacts.net/spec/1.0',
-          "xmlns:ostatus": 'http://ostatus.org/schema/1.0'
-        ],
-        simple_form
-      }
-    ]
-  end
-end
diff --git a/lib/pleroma/web/ostatus/feed_representer.ex b/lib/pleroma/web/ostatus/feed_representer.ex
deleted file mode 100644 (file)
index b7b97e5..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.FeedRepresenter do
-  alias Pleroma.User
-  alias Pleroma.Web.MediaProxy
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.OStatus.ActivityRepresenter
-  alias Pleroma.Web.OStatus.UserRepresenter
-
-  def to_simple_form(user, activities, _users) do
-    most_recent_update =
-      (List.first(activities) || user).updated_at
-      |> NaiveDateTime.to_iso8601()
-
-    h = fn str -> [to_charlist(str)] end
-
-    last_activity = List.last(activities)
-
-    entries =
-      activities
-      |> Enum.map(fn activity ->
-        {:entry, ActivityRepresenter.to_simple_form(activity, user)}
-      end)
-      |> Enum.filter(fn {_, form} -> form end)
-
-    [
-      {
-        :feed,
-        [
-          xmlns: 'http://www.w3.org/2005/Atom',
-          "xmlns:thr": 'http://purl.org/syndication/thread/1.0',
-          "xmlns:activity": 'http://activitystrea.ms/spec/1.0/',
-          "xmlns:poco": 'http://portablecontacts.net/spec/1.0',
-          "xmlns:ostatus": 'http://ostatus.org/schema/1.0'
-        ],
-        [
-          {:id, h.(OStatus.feed_path(user))},
-          {:title, ['#{user.nickname}\'s timeline']},
-          {:updated, h.(most_recent_update)},
-          {:logo, [to_charlist(User.avatar_url(user) |> MediaProxy.url())]},
-          {:link, [rel: 'hub', href: h.(OStatus.pubsub_path(user))], []},
-          {:link, [rel: 'salmon', href: h.(OStatus.salmon_path(user))], []},
-          {:link, [rel: 'self', href: h.(OStatus.feed_path(user)), type: 'application/atom+xml'],
-           []},
-          {:author, UserRepresenter.to_simple_form(user)}
-        ] ++
-          if last_activity do
-            [
-              {:link,
-               [
-                 rel: 'next',
-                 href:
-                   to_charlist(OStatus.feed_path(user)) ++
-                     '?max_id=' ++ to_charlist(last_activity.id),
-                 type: 'application/atom+xml'
-               ], []}
-            ]
-          else
-            []
-          end ++ entries
-      }
-    ]
-  end
-end
diff --git a/lib/pleroma/web/ostatus/handlers/delete_handler.ex b/lib/pleroma/web/ostatus/handlers/delete_handler.ex
deleted file mode 100644 (file)
index ac2dc11..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.DeleteHandler do
-  require Logger
-  alias Pleroma.Object
-  alias Pleroma.Web.ActivityPub.ActivityPub
-  alias Pleroma.Web.XML
-
-  def handle_delete(entry, _doc \\ nil) do
-    with id <- XML.string_from_xpath("//id", entry),
-         %Object{} = object <- Object.normalize(id),
-         {:ok, delete} <- ActivityPub.delete(object, local: false) do
-      delete
-    end
-  end
-end
diff --git a/lib/pleroma/web/ostatus/handlers/follow_handler.ex b/lib/pleroma/web/ostatus/handlers/follow_handler.ex
deleted file mode 100644 (file)
index 2451397..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.FollowHandler do
-  alias Pleroma.User
-  alias Pleroma.Web.ActivityPub.ActivityPub
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.XML
-
-  def handle(entry, doc) do
-    with {:ok, actor} <- OStatus.find_make_or_update_actor(doc),
-         id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry),
-         followed_uri when not is_nil(followed_uri) <-
-           XML.string_from_xpath("/entry/activity:object/id", entry),
-         {:ok, followed} <- OStatus.find_or_make_user(followed_uri),
-         {:locked, false} <- {:locked, followed.info.locked},
-         {:ok, activity} <- ActivityPub.follow(actor, followed, id, false) do
-      User.follow(actor, followed)
-      {:ok, activity}
-    else
-      {:locked, true} ->
-        {:error, "It's not possible to follow locked accounts over OStatus"}
-    end
-  end
-end
diff --git a/lib/pleroma/web/ostatus/handlers/note_handler.ex b/lib/pleroma/web/ostatus/handlers/note_handler.ex
deleted file mode 100644 (file)
index 7fae14f..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.NoteHandler do
-  require Logger
-  require Pleroma.Constants
-
-  alias Pleroma.Activity
-  alias Pleroma.Object
-  alias Pleroma.Web.ActivityPub.ActivityPub
-  alias Pleroma.Web.ActivityPub.Utils
-  alias Pleroma.Web.CommonAPI
-  alias Pleroma.Web.Federator
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.XML
-
-  @doc """
-  Get the context for this note. Uses this:
-  1. The context of the parent activity
-  2. The conversation reference in the ostatus xml
-  3. A newly generated context id.
-  """
-  def get_context(entry, in_reply_to) do
-    context =
-      (XML.string_from_xpath("//ostatus:conversation[1]", entry) ||
-         XML.string_from_xpath("//ostatus:conversation[1]/@ref", entry) || "")
-      |> String.trim()
-
-    with %{data: %{"context" => context}} <- Object.get_cached_by_ap_id(in_reply_to) do
-      context
-    else
-      _e ->
-        if String.length(context) > 0 do
-          context
-        else
-          Utils.generate_context_id()
-        end
-    end
-  end
-
-  def get_people_mentions(entry) do
-    :xmerl_xpath.string(
-      '//link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/person"]',
-      entry
-    )
-    |> Enum.map(fn person -> XML.string_from_xpath("@href", person) end)
-  end
-
-  def get_collection_mentions(entry) do
-    transmogrify = fn
-      "http://activityschema.org/collection/public" ->
-        Pleroma.Constants.as_public()
-
-      group ->
-        group
-    end
-
-    :xmerl_xpath.string(
-      '//link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/collection"]',
-      entry
-    )
-    |> Enum.map(fn collection -> XML.string_from_xpath("@href", collection) |> transmogrify.() end)
-  end
-
-  def get_mentions(entry) do
-    (get_people_mentions(entry) ++ get_collection_mentions(entry))
-    |> Enum.filter(& &1)
-  end
-
-  def get_emoji(entry) do
-    try do
-      :xmerl_xpath.string('//link[@rel="emoji"]', entry)
-      |> Enum.reduce(%{}, fn emoji, acc ->
-        Map.put(acc, XML.string_from_xpath("@name", emoji), XML.string_from_xpath("@href", emoji))
-      end)
-    rescue
-      _e -> nil
-    end
-  end
-
-  def make_to_list(actor, mentions) do
-    [
-      actor.follower_address
-    ] ++ mentions
-  end
-
-  def add_external_url(note, entry) do
-    url = XML.string_from_xpath("//link[@rel='alternate' and @type='text/html']/@href", entry)
-    Map.put(note, "external_url", url)
-  end
-
-  def fetch_replied_to_activity(entry, in_reply_to, options \\ []) do
-    with %Activity{} = activity <- Activity.get_create_by_object_ap_id(in_reply_to) do
-      activity
-    else
-      _e ->
-        with true <- Federator.allowed_incoming_reply_depth?(options[:depth]),
-             in_reply_to_href when not is_nil(in_reply_to_href) <-
-               XML.string_from_xpath("//thr:in-reply-to[1]/@href", entry),
-             {:ok, [activity | _]} <- OStatus.fetch_activity_from_url(in_reply_to_href, options) do
-          activity
-        else
-          _e -> nil
-        end
-    end
-  end
-
-  # TODO: Clean this up a bit.
-  def handle_note(entry, doc \\ nil, options \\ []) do
-    with id <- XML.string_from_xpath("//id", entry),
-         activity when is_nil(activity) <- Activity.get_create_by_object_ap_id_with_object(id),
-         [author] <- :xmerl_xpath.string('//author[1]', doc),
-         {:ok, actor} <- OStatus.find_make_or_update_actor(author),
-         content_html <- OStatus.get_content(entry),
-         cw <- OStatus.get_cw(entry),
-         in_reply_to <- XML.string_from_xpath("//thr:in-reply-to[1]/@ref", entry),
-         options <- Keyword.put(options, :depth, (options[:depth] || 0) + 1),
-         in_reply_to_activity <- fetch_replied_to_activity(entry, in_reply_to, options),
-         in_reply_to_object <-
-           (in_reply_to_activity && Object.normalize(in_reply_to_activity)) || nil,
-         in_reply_to <- (in_reply_to_object && in_reply_to_object.data["id"]) || in_reply_to,
-         attachments <- OStatus.get_attachments(entry),
-         context <- get_context(entry, in_reply_to),
-         tags <- OStatus.get_tags(entry),
-         mentions <- get_mentions(entry),
-         to <- make_to_list(actor, mentions),
-         date <- XML.string_from_xpath("//published", entry),
-         unlisted <- XML.string_from_xpath("//mastodon:scope", entry) == "unlisted",
-         cc <- if(unlisted, do: [Pleroma.Constants.as_public()], else: []),
-         note <-
-           CommonAPI.Utils.make_note_data(
-             actor.ap_id,
-             to,
-             context,
-             content_html,
-             attachments,
-             in_reply_to_activity,
-             [],
-             cw
-           ),
-         note <- note |> Map.put("id", id) |> Map.put("tag", tags),
-         note <- note |> Map.put("published", date),
-         note <- note |> Map.put("emoji", get_emoji(entry)),
-         note <- add_external_url(note, entry),
-         note <- note |> Map.put("cc", cc),
-         # TODO: Handle this case in make_note_data
-         note <-
-           if(
-             in_reply_to && !in_reply_to_activity,
-             do: note |> Map.put("inReplyTo", in_reply_to),
-             else: note
-           ) do
-      ActivityPub.create(%{
-        to: to,
-        actor: actor,
-        context: context,
-        object: note,
-        published: date,
-        local: false,
-        additional: %{"cc" => cc}
-      })
-    else
-      %Activity{} = activity -> {:ok, activity}
-      e -> {:error, e}
-    end
-  end
-end
diff --git a/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex b/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex
deleted file mode 100644 (file)
index 2062432..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.UnfollowHandler do
-  alias Pleroma.User
-  alias Pleroma.Web.ActivityPub.ActivityPub
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.XML
-
-  def handle(entry, doc) do
-    with {:ok, actor} <- OStatus.find_make_or_update_actor(doc),
-         id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry),
-         followed_uri when not is_nil(followed_uri) <-
-           XML.string_from_xpath("/entry/activity:object/id", entry),
-         {:ok, followed} <- OStatus.find_or_make_user(followed_uri),
-         {:ok, activity} <- ActivityPub.unfollow(actor, followed, id, false) do
-      User.unfollow(actor, followed)
-      {:ok, activity}
-    end
-  end
-end
diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex
deleted file mode 100644 (file)
index 5de1cee..0000000
+++ /dev/null
@@ -1,395 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus do
-  import Pleroma.Web.XML
-  require Logger
-
-  alias Pleroma.Activity
-  alias Pleroma.HTTP
-  alias Pleroma.Object
-  alias Pleroma.User
-  alias Pleroma.Web
-  alias Pleroma.Web.ActivityPub.ActivityPub
-  alias Pleroma.Web.ActivityPub.Transmogrifier
-  alias Pleroma.Web.ActivityPub.Visibility
-  alias Pleroma.Web.OStatus.DeleteHandler
-  alias Pleroma.Web.OStatus.FollowHandler
-  alias Pleroma.Web.OStatus.NoteHandler
-  alias Pleroma.Web.OStatus.UnfollowHandler
-  alias Pleroma.Web.WebFinger
-  alias Pleroma.Web.Websub
-
-  def is_representable?(%Activity{} = activity) do
-    object = Object.normalize(activity)
-
-    cond do
-      is_nil(object) ->
-        false
-
-      Visibility.is_public?(activity) && object.data["type"] == "Note" ->
-        true
-
-      true ->
-        false
-    end
-  end
-
-  def feed_path(user), do: "#{user.ap_id}/feed.atom"
-
-  def pubsub_path(user), do: "#{Web.base_url()}/push/hub/#{user.nickname}"
-
-  def salmon_path(user), do: "#{user.ap_id}/salmon"
-
-  def remote_follow_path, do: "#{Web.base_url()}/ostatus_subscribe?acct={uri}"
-
-  def handle_incoming(xml_string, options \\ []) do
-    with doc when doc != :error <- parse_document(xml_string) do
-      with {:ok, actor_user} <- find_make_or_update_actor(doc),
-           do: Pleroma.Instances.set_reachable(actor_user.ap_id)
-
-      entries = :xmerl_xpath.string('//entry', doc)
-
-      activities =
-        Enum.map(entries, fn entry ->
-          {:xmlObj, :string, object_type} =
-            :xmerl_xpath.string('string(/entry/activity:object-type[1])', entry)
-
-          {:xmlObj, :string, verb} = :xmerl_xpath.string('string(/entry/activity:verb[1])', entry)
-          Logger.debug("Handling #{verb}")
-
-          try do
-            case verb do
-              'http://activitystrea.ms/schema/1.0/delete' ->
-                with {:ok, activity} <- DeleteHandler.handle_delete(entry, doc), do: activity
-
-              'http://activitystrea.ms/schema/1.0/follow' ->
-                with {:ok, activity} <- FollowHandler.handle(entry, doc), do: activity
-
-              'http://activitystrea.ms/schema/1.0/unfollow' ->
-                with {:ok, activity} <- UnfollowHandler.handle(entry, doc), do: activity
-
-              'http://activitystrea.ms/schema/1.0/share' ->
-                with {:ok, activity, retweeted_activity} <- handle_share(entry, doc),
-                     do: [activity, retweeted_activity]
-
-              'http://activitystrea.ms/schema/1.0/favorite' ->
-                with {:ok, activity, favorited_activity} <- handle_favorite(entry, doc),
-                     do: [activity, favorited_activity]
-
-              _ ->
-                case object_type do
-                  'http://activitystrea.ms/schema/1.0/note' ->
-                    with {:ok, activity} <- NoteHandler.handle_note(entry, doc, options),
-                         do: activity
-
-                  'http://activitystrea.ms/schema/1.0/comment' ->
-                    with {:ok, activity} <- NoteHandler.handle_note(entry, doc, options),
-                         do: activity
-
-                  _ ->
-                    Logger.error("Couldn't parse incoming document")
-                    nil
-                end
-            end
-          rescue
-            e ->
-              Logger.error("Error occured while handling activity")
-              Logger.error(xml_string)
-              Logger.error(inspect(e))
-              nil
-          end
-        end)
-        |> Enum.filter(& &1)
-
-      {:ok, activities}
-    else
-      _e -> {:error, []}
-    end
-  end
-
-  def make_share(entry, doc, retweeted_activity) do
-    with {:ok, actor} <- find_make_or_update_actor(doc),
-         %Object{} = object <- Object.normalize(retweeted_activity),
-         id when not is_nil(id) <- string_from_xpath("/entry/id", entry),
-         {:ok, activity, _object} = ActivityPub.announce(actor, object, id, false) do
-      {:ok, activity}
-    end
-  end
-
-  def handle_share(entry, doc) do
-    with {:ok, retweeted_activity} <- get_or_build_object(entry),
-         {:ok, activity} <- make_share(entry, doc, retweeted_activity) do
-      {:ok, activity, retweeted_activity}
-    else
-      e -> {:error, e}
-    end
-  end
-
-  def make_favorite(entry, doc, favorited_activity) do
-    with {:ok, actor} <- find_make_or_update_actor(doc),
-         %Object{} = object <- Object.normalize(favorited_activity),
-         id when not is_nil(id) <- string_from_xpath("/entry/id", entry),
-         {:ok, activity, _object} = ActivityPub.like(actor, object, id, false) do
-      {:ok, activity}
-    end
-  end
-
-  def get_or_build_object(entry) do
-    with {:ok, activity} <- get_or_try_fetching(entry) do
-      {:ok, activity}
-    else
-      _e ->
-        with [object] <- :xmerl_xpath.string('/entry/activity:object', entry) do
-          NoteHandler.handle_note(object, object)
-        end
-    end
-  end
-
-  def get_or_try_fetching(entry) do
-    Logger.debug("Trying to get entry from db")
-
-    with id when not is_nil(id) <- string_from_xpath("//activity:object[1]/id", entry),
-         %Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do
-      {:ok, activity}
-    else
-      _ ->
-        Logger.debug("Couldn't get, will try to fetch")
-
-        with href when not is_nil(href) <-
-               string_from_xpath("//activity:object[1]/link[@type=\"text/html\"]/@href", entry),
-             {:ok, [favorited_activity]} <- fetch_activity_from_url(href) do
-          {:ok, favorited_activity}
-        else
-          e -> Logger.debug("Couldn't find href: #{inspect(e)}")
-        end
-    end
-  end
-
-  def handle_favorite(entry, doc) do
-    with {:ok, favorited_activity} <- get_or_try_fetching(entry),
-         {:ok, activity} <- make_favorite(entry, doc, favorited_activity) do
-      {:ok, activity, favorited_activity}
-    else
-      e -> {:error, e}
-    end
-  end
-
-  def get_attachments(entry) do
-    :xmerl_xpath.string('/entry/link[@rel="enclosure"]', entry)
-    |> Enum.map(fn enclosure ->
-      with href when not is_nil(href) <- string_from_xpath("/link/@href", enclosure),
-           type when not is_nil(type) <- string_from_xpath("/link/@type", enclosure) do
-        %{
-          "type" => "Attachment",
-          "url" => [
-            %{
-              "type" => "Link",
-              "mediaType" => type,
-              "href" => href
-            }
-          ]
-        }
-      end
-    end)
-    |> Enum.filter(& &1)
-  end
-
-  @doc """
-    Gets the content from a an entry.
-  """
-  def get_content(entry) do
-    string_from_xpath("//content", entry)
-  end
-
-  @doc """
-    Get the cw that mastodon uses.
-  """
-  def get_cw(entry) do
-    case string_from_xpath("/*/summary", entry) do
-      cw when not is_nil(cw) -> cw
-      _ -> nil
-    end
-  end
-
-  def get_tags(entry) do
-    :xmerl_xpath.string('//category', entry)
-    |> Enum.map(fn category -> string_from_xpath("/category/@term", category) end)
-    |> Enum.filter(& &1)
-    |> Enum.map(&String.downcase/1)
-  end
-
-  def maybe_update(doc, user) do
-    case string_from_xpath("//author[1]/ap_enabled", doc) do
-      "true" ->
-        Transmogrifier.upgrade_user_from_ap_id(user.ap_id)
-
-      _ ->
-        maybe_update_ostatus(doc, user)
-    end
-  end
-
-  def maybe_update_ostatus(doc, user) do
-    old_data = Map.take(user, [:bio, :avatar, :name])
-
-    with false <- user.local,
-         avatar <- make_avatar_object(doc),
-         bio <- string_from_xpath("//author[1]/summary", doc),
-         name <- string_from_xpath("//author[1]/poco:displayName", doc),
-         new_data <- %{
-           avatar: avatar || old_data.avatar,
-           name: name || old_data.name,
-           bio: bio || old_data.bio
-         },
-         false <- new_data == old_data do
-      change = Ecto.Changeset.change(user, new_data)
-      User.update_and_set_cache(change)
-    else
-      _ ->
-        {:ok, user}
-    end
-  end
-
-  def find_make_or_update_actor(doc) do
-    uri = string_from_xpath("//author/uri[1]", doc)
-
-    with {:ok, %User{} = user} <- find_or_make_user(uri),
-         {:ap_enabled, false} <- {:ap_enabled, User.ap_enabled?(user)} do
-      maybe_update(doc, user)
-    else
-      {:ap_enabled, true} ->
-        {:error, :invalid_protocol}
-
-      _ ->
-        {:error, :unknown_user}
-    end
-  end
-
-  @spec find_or_make_user(String.t()) :: {:ok, User.t()}
-  def find_or_make_user(uri) do
-    case User.get_by_ap_id(uri) do
-      %User{} = user -> {:ok, user}
-      _ -> make_user(uri)
-    end
-  end
-
-  @spec make_user(String.t(), boolean()) :: {:ok, User.t()} | {:error, any()}
-  def make_user(uri, update \\ false) do
-    with {:ok, info} <- gather_user_info(uri) do
-      with false <- update,
-           %User{} = user <- User.get_cached_by_ap_id(info["uri"]) do
-        {:ok, user}
-      else
-        _e -> User.insert_or_update_user(build_user_data(info))
-      end
-    end
-  end
-
-  defp build_user_data(info) do
-    %{
-      name: info["name"],
-      nickname: info["nickname"] <> "@" <> info["host"],
-      ap_id: info["uri"],
-      info: info,
-      avatar: info["avatar"],
-      bio: info["bio"]
-    }
-  end
-
-  # TODO: Just takes the first one for now.
-  def make_avatar_object(author_doc, rel \\ "avatar") do
-    href = string_from_xpath("//author[1]/link[@rel=\"#{rel}\"]/@href", author_doc)
-    type = string_from_xpath("//author[1]/link[@rel=\"#{rel}\"]/@type", author_doc)
-
-    if href do
-      %{
-        "type" => "Image",
-        "url" => [%{"type" => "Link", "mediaType" => type, "href" => href}]
-      }
-    else
-      nil
-    end
-  end
-
-  @spec gather_user_info(String.t()) :: {:ok, map()} | {:error, any()}
-  def gather_user_info(username) do
-    with {:ok, webfinger_data} <- WebFinger.finger(username),
-         {:ok, feed_data} <- Websub.gather_feed_data(webfinger_data["topic"]) do
-      data =
-        webfinger_data
-        |> Map.merge(feed_data)
-        |> Map.put("fqn", username)
-
-      {:ok, data}
-    else
-      e ->
-        Logger.debug(fn -> "Couldn't gather info for #{username}" end)
-        {:error, e}
-    end
-  end
-
-  # Regex-based 'parsing' so we don't have to pull in a full html parser
-  # It's a hack anyway. Maybe revisit this in the future
-  @mastodon_regex ~r/<link href='(.*)' rel='alternate' type='application\/atom\+xml'>/
-  @gs_regex ~r/<link title=.* href="(.*)" type="application\/atom\+xml" rel="alternate">/
-  @gs_classic_regex ~r/<link rel="alternate" href="(.*)" type="application\/atom\+xml" title=.*>/
-  def get_atom_url(body) do
-    cond do
-      Regex.match?(@mastodon_regex, body) ->
-        [[_, match]] = Regex.scan(@mastodon_regex, body)
-        {:ok, match}
-
-      Regex.match?(@gs_regex, body) ->
-        [[_, match]] = Regex.scan(@gs_regex, body)
-        {:ok, match}
-
-      Regex.match?(@gs_classic_regex, body) ->
-        [[_, match]] = Regex.scan(@gs_classic_regex, body)
-        {:ok, match}
-
-      true ->
-        Logger.debug(fn -> "Couldn't find Atom link in #{inspect(body)}" end)
-        {:error, "Couldn't find the Atom link"}
-    end
-  end
-
-  def fetch_activity_from_atom_url(url, options \\ []) do
-    with true <- String.starts_with?(url, "http"),
-         {:ok, %{body: body, status: code}} when code in 200..299 <-
-           HTTP.get(url, [{:Accept, "application/atom+xml"}]) do
-      Logger.debug("Got document from #{url}, handling...")
-      handle_incoming(body, options)
-    else
-      e ->
-        Logger.debug("Couldn't get #{url}: #{inspect(e)}")
-        e
-    end
-  end
-
-  def fetch_activity_from_html_url(url, options \\ []) do
-    Logger.debug("Trying to fetch #{url}")
-
-    with true <- String.starts_with?(url, "http"),
-         {:ok, %{body: body}} <- HTTP.get(url, []),
-         {:ok, atom_url} <- get_atom_url(body) do
-      fetch_activity_from_atom_url(atom_url, options)
-    else
-      e ->
-        Logger.debug("Couldn't get #{url}: #{inspect(e)}")
-        e
-    end
-  end
-
-  def fetch_activity_from_url(url, options \\ []) do
-    with {:ok, [_ | _] = activities} <- fetch_activity_from_atom_url(url, options) do
-      {:ok, activities}
-    else
-      _e -> fetch_activity_from_html_url(url, options)
-    end
-  rescue
-    e ->
-      Logger.debug("Couldn't get #{url}: #{inspect(e)}")
-      {:error, "Couldn't get #{url}: #{inspect(e)}"}
-  end
-end
index 20f2d9ddc58ef507ccec9384569cb9ec7cc56008..6958519de05d95acfadeecd749af7a52ec82d778 100644 (file)
@@ -13,19 +13,14 @@ defmodule Pleroma.Web.OStatus.OStatusController do
   alias Pleroma.Web.ActivityPub.ObjectView
   alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.Endpoint
-  alias Pleroma.Web.Federator
   alias Pleroma.Web.Metadata.PlayerView
-  alias Pleroma.Web.OStatus.ActivityRepresenter
   alias Pleroma.Web.Router
-  alias Pleroma.Web.XML
 
   plug(
     Pleroma.Plugs.RateLimiter,
     {:ap_routes, params: ["uuid"]} when action in [:object, :activity]
   )
 
-  plug(Pleroma.Web.FederatingPlug when action in [:salmon_incoming])
-
   plug(
     Pleroma.Plugs.SetFormatPlug
     when action in [:object, :activity, :notice]
@@ -33,32 +28,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do
 
   action_fallback(:errors)
 
-  defp decode_or_retry(body) do
-    with {:ok, magic_key} <- Pleroma.Web.Salmon.fetch_magic_key(body),
-         {:ok, doc} <- Pleroma.Web.Salmon.decode_and_validate(magic_key, body) do
-      {:ok, doc}
-    else
-      _e ->
-        with [decoded | _] <- Pleroma.Web.Salmon.decode(body),
-             doc <- XML.parse_document(decoded),
-             uri when not is_nil(uri) <- XML.string_from_xpath("/entry/author[1]/uri", doc),
-             {:ok, _} <- Pleroma.Web.OStatus.make_user(uri, true),
-             {:ok, magic_key} <- Pleroma.Web.Salmon.fetch_magic_key(body),
-             {:ok, doc} <- Pleroma.Web.Salmon.decode_and_validate(magic_key, body) do
-          {:ok, doc}
-        end
-    end
-  end
-
-  def salmon_incoming(conn, _) do
-    {:ok, body, _conn} = read_body(conn)
-    {:ok, doc} = decode_or_retry(body)
-
-    Federator.incoming_doc(doc)
-
-    send_resp(conn, 200, "")
-  end
-
   def object(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
       when format in ["json", "activity+json"] do
     ActivityPubController.call(conn, :object)
@@ -179,23 +148,10 @@ defmodule Pleroma.Web.OStatus.OStatusController do
     |> render("object.json", %{object: object})
   end
 
-  defp represent_activity(_conn, "activity+json", _, _) do
+  defp represent_activity(_conn, _, _, _) do
     {:error, :not_found}
   end
 
-  defp represent_activity(conn, _, activity, user) do
-    response =
-      activity
-      |> ActivityRepresenter.to_simple_form(user, true)
-      |> ActivityRepresenter.wrap_with_entry()
-      |> :xmerl.export_simple(:xmerl_xml)
-      |> to_string
-
-    conn
-    |> put_resp_content_type("application/atom+xml")
-    |> send_resp(200, response)
-  end
-
   def errors(conn, {:error, :not_found}) do
     render_error(conn, :not_found, "Not found")
   end
diff --git a/lib/pleroma/web/ostatus/user_representer.ex b/lib/pleroma/web/ostatus/user_representer.ex
deleted file mode 100644 (file)
index 852be6e..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.UserRepresenter do
-  alias Pleroma.User
-
-  def to_simple_form(user) do
-    ap_id = to_charlist(user.ap_id)
-    nickname = to_charlist(user.nickname)
-    name = to_charlist(user.name)
-    bio = to_charlist(user.bio)
-    avatar_url = to_charlist(User.avatar_url(user))
-
-    banner =
-      if banner_url = User.banner_url(user) do
-        [{:link, [rel: 'header', href: banner_url], []}]
-      else
-        []
-      end
-
-    ap_enabled =
-      if user.local do
-        [{:ap_enabled, ['true']}]
-      else
-        []
-      end
-
-    [
-      {:id, [ap_id]},
-      {:"activity:object", ['http://activitystrea.ms/schema/1.0/person']},
-      {:uri, [ap_id]},
-      {:"poco:preferredUsername", [nickname]},
-      {:"poco:displayName", [name]},
-      {:"poco:note", [bio]},
-      {:summary, [bio]},
-      {:name, [nickname]},
-      {:link, [rel: 'avatar', href: avatar_url], []}
-    ] ++ banner ++ ap_enabled
-  end
-end
index 9012e2175e5ef7231e626b4c3f560d22fd761b45..ee40bbf33ab7a130038afe1eb7d5976778c39599 100644 (file)
@@ -80,9 +80,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
 
   @doc "PATCH /api/v1/pleroma/accounts/update_banner"
   def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
-    new_info = %{"banner" => %{}}
-
-    with {:ok, user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do
+    with {:ok, user} <- User.update_banner(user, %{}) do
       CommonAPI.update(user)
       json(conn, %{url: nil})
     end
@@ -90,8 +88,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
 
   def update_banner(%{assigns: %{user: user}} = conn, params) do
     with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
-         new_info <- %{"banner" => object.data},
-         {:ok, user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do
+         {:ok, user} <- User.update_banner(user, object.data) do
       CommonAPI.update(user)
       %{"url" => [%{"href" => href} | _]} = object.data
 
@@ -101,17 +98,14 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
 
   @doc "PATCH /api/v1/pleroma/accounts/update_background"
   def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
-    new_info = %{"background" => %{}}
-
-    with {:ok, _user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do
+    with {:ok, _user} <- User.update_background(user, %{}) do
       json(conn, %{url: nil})
     end
   end
 
   def update_background(%{assigns: %{user: user}} = conn, params) do
     with {:ok, object} <- ActivityPub.upload(params, type: :background),
-         new_info <- %{"background" => object.data},
-         {:ok, _user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do
+         {:ok, _user} <- User.update_background(user, object.data) do
       %{"url" => [%{"href" => href} | _]} = object.data
 
       json(conn, %{url: href})
@@ -119,7 +113,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
   end
 
   @doc "GET /api/v1/pleroma/accounts/:id/favourites"
-  def favourites(%{assigns: %{account: %{info: %{hide_favorites: true}}}} = conn, _params) do
+  def favourites(%{assigns: %{account: %{hide_favorites: true}}} = conn, _params) do
     render_error(conn, :forbidden, "Can't get favorites")
   end
 
index d71d72dd5a1b6da7e3e3248cae09f4ce77924338..8cf552b7e230d77816235588d4ea1d6f35844bd8 100644 (file)
@@ -24,9 +24,7 @@ defmodule Pleroma.Web.PleromaAPI.MascotController do
     with {:ok, object} <- ActivityPub.upload(file, actor: User.ap_id(user)),
          # Reject if not an image
          %{type: "image"} = attachment <- render_attachment(object) do
-      # Sure!
-      # Save to the user's info
-      {:ok, _user} = User.update_info(user, &User.Info.mascot_update(&1, attachment))
+      {:ok, _user} = User.mascot_update(user, attachment)
 
       json(conn, attachment)
     else
index 9d50a7ca996c72f61061fa2e708917d1877eb8f6..651a9942382355871610cc687cb7dec48ab20199 100644 (file)
@@ -79,6 +79,15 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
     end
   end
 
+  def read_conversations(%{assigns: %{user: user}} = conn, _params) do
+    with {:ok, _, participations} <- Participation.mark_all_as_read(user) do
+      conn
+      |> add_link_headers(participations)
+      |> put_view(ConversationView)
+      |> render("participations.json", participations: participations, for: user)
+    end
+  end
+
   def read_notification(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
     with {:ok, notification} <- Notification.read_one(user, notification_id) do
       conn
index 35d3ff07cbb4946b411eac8f040efb454931dd76..dd445e8bfe2d0877ebbe850b304b5ef6952f2979 100644 (file)
@@ -125,6 +125,10 @@ defmodule Pleroma.Web.Push.Impl do
     end
   end
 
+  def format_title(%{activity: %{data: %{"directMessage" => true}}}) do
+    "New Direct Message"
+  end
+
   def format_title(%{activity: %{data: %{"type" => type}}}) do
     case type do
       "Create" -> "New Mention"
index ae799b8ac36650112f23e7756d0cf475754557a0..f69c5c2bc610799d8725efcbb63852a83de3105c 100644 (file)
@@ -137,11 +137,14 @@ defmodule Pleroma.Web.Router do
     delete("/users", AdminAPIController, :user_delete)
     post("/users", AdminAPIController, :users_create)
     patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
+    patch("/users/activate", AdminAPIController, :user_activate)
+    patch("/users/deactivate", AdminAPIController, :user_deactivate)
     put("/users/tag", AdminAPIController, :tag_users)
     delete("/users/tag", AdminAPIController, :untag_users)
 
     get("/users/:nickname/permission_group", AdminAPIController, :right_get)
     get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get)
+
     post("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_add)
 
     delete(
@@ -150,8 +153,15 @@ defmodule Pleroma.Web.Router do
       :right_delete
     )
 
-    put("/users/:nickname/activation_status", AdminAPIController, :set_activation_status)
+    post("/users/permission_group/:permission_group", AdminAPIController, :right_add_multiple)
+
+    delete(
+      "/users/permission_group/:permission_group",
+      AdminAPIController,
+      :right_delete_multiple
+    )
 
+    get("/relay", AdminAPIController, :relay_list)
     post("/relay", AdminAPIController, :relay_follow)
     delete("/relay", AdminAPIController, :relay_unfollow)
 
@@ -256,6 +266,7 @@ defmodule Pleroma.Web.Router do
 
       get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses)
       get("/conversations/:id", PleromaAPIController, :conversation)
+      post("/conversations/read", PleromaAPIController, :read_conversations)
     end
 
     scope [] do
@@ -394,6 +405,9 @@ defmodule Pleroma.Web.Router do
     get("/push/subscription", SubscriptionController, :get)
     put("/push/subscription", SubscriptionController, :update)
     delete("/push/subscription", SubscriptionController, :delete)
+
+    get("/markers", MarkerController, :index)
+    post("/markers", MarkerController, :upsert)
   end
 
   scope "/api/web", Pleroma.Web do
@@ -499,11 +513,6 @@ defmodule Pleroma.Web.Router do
     get("/users/:nickname/feed", Feed.FeedController, :feed)
     get("/users/:nickname", Feed.FeedController, :feed_redirect)
 
-    post("/users/:nickname/salmon", OStatus.OStatusController, :salmon_incoming)
-    post("/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request)
-    get("/push/subscriptions/:id", Websub.WebsubController, :websub_subscription_confirmation)
-    post("/push/subscriptions/:id", Websub.WebsubController, :websub_incoming)
-
     get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
   end
 
@@ -586,6 +595,12 @@ defmodule Pleroma.Web.Router do
     get("/:version", Nodeinfo.NodeinfoController, :nodeinfo)
   end
 
+  scope "/", Pleroma.Web do
+    pipe_through(:api)
+
+    get("/web/manifest.json", MastoFEController, :manifest)
+  end
+
   scope "/", Pleroma.Web do
     pipe_through(:mastodon_html)
 
diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex
deleted file mode 100644 (file)
index 0ffe903..0000000
+++ /dev/null
@@ -1,254 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Salmon do
-  @behaviour Pleroma.Web.Federator.Publisher
-
-  use Bitwise
-
-  alias Pleroma.Activity
-  alias Pleroma.HTTP
-  alias Pleroma.Instances
-  alias Pleroma.Keys
-  alias Pleroma.User
-  alias Pleroma.Web.ActivityPub.Visibility
-  alias Pleroma.Web.Federator.Publisher
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.OStatus.ActivityRepresenter
-  alias Pleroma.Web.XML
-
-  require Logger
-
-  def decode(salmon) do
-    doc = XML.parse_document(salmon)
-
-    {:xmlObj, :string, data} = :xmerl_xpath.string('string(//me:data[1])', doc)
-    {:xmlObj, :string, sig} = :xmerl_xpath.string('string(//me:sig[1])', doc)
-    {:xmlObj, :string, alg} = :xmerl_xpath.string('string(//me:alg[1])', doc)
-    {:xmlObj, :string, encoding} = :xmerl_xpath.string('string(//me:encoding[1])', doc)
-    {:xmlObj, :string, type} = :xmerl_xpath.string('string(//me:data[1]/@type)', doc)
-
-    {:ok, data} = Base.url_decode64(to_string(data), ignore: :whitespace)
-    {:ok, sig} = Base.url_decode64(to_string(sig), ignore: :whitespace)
-    alg = to_string(alg)
-    encoding = to_string(encoding)
-    type = to_string(type)
-
-    [data, type, encoding, alg, sig]
-  end
-
-  def fetch_magic_key(salmon) do
-    with [data, _, _, _, _] <- decode(salmon),
-         doc <- XML.parse_document(data),
-         uri when not is_nil(uri) <- XML.string_from_xpath("/entry/author[1]/uri", doc),
-         {:ok, public_key} <- User.get_public_key_for_ap_id(uri),
-         magic_key <- encode_key(public_key) do
-      {:ok, magic_key}
-    end
-  end
-
-  def decode_and_validate(magickey, salmon) do
-    [data, type, encoding, alg, sig] = decode(salmon)
-
-    signed_text =
-      [data, type, encoding, alg]
-      |> Enum.map(&Base.url_encode64/1)
-      |> Enum.join(".")
-
-    key = decode_key(magickey)
-
-    verify = :public_key.verify(signed_text, :sha256, sig, key)
-
-    if verify do
-      {:ok, data}
-    else
-      :error
-    end
-  end
-
-  def decode_key("RSA." <> magickey) do
-    make_integer = fn bin ->
-      list = :erlang.binary_to_list(bin)
-      Enum.reduce(list, 0, fn el, acc -> acc <<< 8 ||| el end)
-    end
-
-    [modulus, exponent] =
-      magickey
-      |> String.split(".")
-      |> Enum.map(fn n -> Base.url_decode64!(n, padding: false) end)
-      |> Enum.map(make_integer)
-
-    {:RSAPublicKey, modulus, exponent}
-  end
-
-  def encode_key({:RSAPublicKey, modulus, exponent}) do
-    modulus_enc = :binary.encode_unsigned(modulus) |> Base.url_encode64()
-    exponent_enc = :binary.encode_unsigned(exponent) |> Base.url_encode64()
-
-    "RSA.#{modulus_enc}.#{exponent_enc}"
-  end
-
-  def encode(private_key, doc) do
-    type = "application/atom+xml"
-    encoding = "base64url"
-    alg = "RSA-SHA256"
-
-    signed_text =
-      [doc, type, encoding, alg]
-      |> Enum.map(&Base.url_encode64/1)
-      |> Enum.join(".")
-
-    signature =
-      signed_text
-      |> :public_key.sign(:sha256, private_key)
-      |> to_string
-      |> Base.url_encode64()
-
-    doc_base64 =
-      doc
-      |> Base.url_encode64()
-
-    # Don't need proper xml building, these strings are safe to leave unescaped
-    salmon = """
-    <?xml version="1.0" encoding="UTF-8"?>
-    <me:env xmlns:me="http://salmon-protocol.org/ns/magic-env">
-      <me:data type="application/atom+xml">#{doc_base64}</me:data>
-      <me:encoding>#{encoding}</me:encoding>
-      <me:alg>#{alg}</me:alg>
-      <me:sig>#{signature}</me:sig>
-    </me:env>
-    """
-
-    {:ok, salmon}
-  end
-
-  def remote_users(%User{id: user_id}, %{data: %{"to" => to} = data}) do
-    cc = Map.get(data, "cc", [])
-
-    bcc =
-      data
-      |> Map.get("bcc", [])
-      |> Enum.reduce([], fn ap_id, bcc ->
-        case Pleroma.List.get_by_ap_id(ap_id) do
-          %Pleroma.List{user_id: ^user_id} = list ->
-            {:ok, following} = Pleroma.List.get_following(list)
-            bcc ++ Enum.map(following, & &1.ap_id)
-
-          _ ->
-            bcc
-        end
-      end)
-
-    [to, cc, bcc]
-    |> Enum.concat()
-    |> Enum.map(&User.get_cached_by_ap_id/1)
-    |> Enum.filter(fn user -> user && !user.local end)
-  end
-
-  @doc "Pushes an activity to remote account."
-  def publish_one(%{recipient: %{info: %{salmon: salmon}}} = params),
-    do: publish_one(Map.put(params, :recipient, salmon))
-
-  def publish_one(%{recipient: url, feed: feed} = params) when is_binary(url) do
-    with {:ok, %{status: code}} when code in 200..299 <-
-           HTTP.post(
-             url,
-             feed,
-             [{"Content-Type", "application/magic-envelope+xml"}]
-           ) do
-      if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since],
-        do: Instances.set_reachable(url)
-
-      Logger.debug(fn -> "Pushed to #{url}, code #{code}" end)
-      {:ok, code}
-    else
-      e ->
-        unless params[:unreachable_since], do: Instances.set_reachable(url)
-        Logger.debug(fn -> "Pushing Salmon to #{url} failed, #{inspect(e)}" end)
-        {:error, "Unreachable instance"}
-    end
-  end
-
-  def publish_one(%{recipient_id: recipient_id} = params) do
-    recipient = User.get_cached_by_id(recipient_id)
-
-    params
-    |> Map.delete(:recipient_id)
-    |> Map.put(:recipient, recipient)
-    |> publish_one()
-  end
-
-  def publish_one(_), do: :noop
-
-  @supported_activities [
-    "Create",
-    "Follow",
-    "Like",
-    "Announce",
-    "Undo",
-    "Delete"
-  ]
-
-  def is_representable?(%Activity{data: %{"type" => type}} = activity)
-      when type in @supported_activities,
-      do: Visibility.is_public?(activity)
-
-  def is_representable?(_), do: false
-
-  @doc """
-  Publishes an activity to remote accounts
-  """
-  @spec publish(User.t(), Pleroma.Activity.t()) :: none
-  def publish(user, activity)
-
-  def publish(%{keys: keys} = user, %{data: %{"type" => type}} = activity)
-      when type in @supported_activities do
-    feed = ActivityRepresenter.to_simple_form(activity, user, true)
-
-    if feed do
-      feed =
-        ActivityRepresenter.wrap_with_entry(feed)
-        |> :xmerl.export_simple(:xmerl_xml)
-        |> to_string
-
-      {:ok, private, _} = Keys.keys_from_pem(keys)
-      {:ok, feed} = encode(private, feed)
-
-      remote_users = remote_users(user, activity)
-
-      salmon_urls = Enum.map(remote_users, & &1.info.salmon)
-      reachable_urls_metadata = Instances.filter_reachable(salmon_urls)
-      reachable_urls = Map.keys(reachable_urls_metadata)
-
-      remote_users
-      |> Enum.filter(&(&1.info.salmon in reachable_urls))
-      |> Enum.each(fn remote_user ->
-        Logger.debug(fn -> "Sending Salmon to #{remote_user.ap_id}" end)
-
-        Publisher.enqueue_one(__MODULE__, %{
-          recipient_id: remote_user.id,
-          feed: feed,
-          unreachable_since: reachable_urls_metadata[remote_user.info.salmon]
-        })
-      end)
-    end
-  end
-
-  def publish(%{id: id}, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end)
-
-  def gather_webfinger_links(%User{} = user) do
-    {:ok, _private, public} = Keys.keys_from_pem(user.keys)
-    magic_key = encode_key(public)
-
-    [
-      %{"rel" => "salmon", "href" => OStatus.salmon_path(user)},
-      %{
-        "rel" => "magic-public-key",
-        "href" => "data:application/magic-public-key,#{magic_key}"
-      }
-    ]
-  end
-
-  def gather_nodeinfo_protocol_names, do: []
-end
index 8cf719277f4774de6197cb20b283c51bc2a3ebfb..2fc7ac8cf694a2b4a6ecd30a3466b0c967fa7ee5 100644 (file)
@@ -49,7 +49,7 @@ defmodule Pleroma.Web.Streamer do
     end
   end
 
-  defp handle_should_send(_) do
-    true
-  end
+  defp handle_should_send(:benchmark), do: false
+
+  defp handle_should_send(_), do: true
 end
index 0ea224874ed9ce430d840ac88e5a296f4f3e3774..c2ee9e1f5c2385cb171f822a0f2295d96254ba74 100644 (file)
@@ -129,12 +129,12 @@ defmodule Pleroma.Web.Streamer.Worker do
   end
 
   defp should_send?(%User{} = user, %Activity{} = item) do
-    blocks = user.info.blocks || []
-    mutes = user.info.mutes || []
-    reblog_mutes = user.info.muted_reblogs || []
+    blocks = user.blocks || []
+    mutes = user.mutes || []
+    reblog_mutes = user.muted_reblogs || []
     recipient_blocks = MapSet.new(blocks ++ mutes)
     recipients = MapSet.new(item.recipients)
-    domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.info.domain_blocks)
+    domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.domain_blocks)
 
     with parent when not is_nil(parent) <- Object.normalize(item),
          true <- Enum.all?([blocks, mutes, reblog_mutes], &(item.actor not in &1)),
@@ -212,7 +212,7 @@ defmodule Pleroma.Web.Streamer.Worker do
   end
 
   @spec thread_containment(Activity.t(), User.t()) :: boolean()
-  defp thread_containment(_activity, %User{info: %{skip_thread_containment: true}}), do: true
+  defp thread_containment(_activity, %User{skip_thread_containment: true}), do: true
 
   defp thread_containment(activity, user) do
     if Config.get([:instance, :skip_thread_containment]) do
index fbfdc46b542505e7fce2e0331b50b03b8184c802..45df9dc09a20594a721d7020e835df2cb0098ba4 100644 (file)
@@ -10,8 +10,6 @@
   <title><%= @user.nickname <> "'s timeline" %></title>
   <updated><%= most_recent_update(@activities, @user) %></updated>
   <logo><%= logo(@user) %></logo>
-  <link rel="hub" href="<%= websub_url(@conn, :websub_subscription_request, @user.nickname) %>"/>
-  <link rel="salmon" href="<%= o_status_url(@conn, :salmon_incoming, @user.nickname) %>"/>
   <link rel="self" href="<%= '#{feed_url(@conn, :feed, @user.nickname)}.atom' %>" type="application/atom+xml"/>
 
   <%= render @view_module, "_author.xml", assigns %>
index feff36fae528efbad0f55d840a58eb81ac4c71b1..c330960fa5e9bf01ce378bb0927e99378dda4db3 100644 (file)
@@ -4,9 +4,13 @@
 <meta charset='utf-8'>
 <meta content='width=device-width, initial-scale=1' name='viewport'>
 <title>
-<%= Pleroma.Config.get([:instance, :name]) %>
+<%= Config.get([:instance, :name]) %>
 </title>
 <link rel="icon" type="image/png" href="/favicon.png"/>
+<link rel="manifest" type="applicaton/manifest+json" href="<%= masto_fe_path(Pleroma.Web.Endpoint, :manifest) %>" />
+
+<meta name="theme-color" content="<%= Config.get([:manifest, :theme_color]) %>" />
+
 <script crossorigin='anonymous' src="/packs/locales.js"></script>
 <script crossorigin='anonymous' src="/packs/locales/glitch/en.js"></script>
 
index bf5a6ae42d87dd7c7f7cb2b09d722f7cab89368c..39f10c49ff91ac6fe46f207505f24d5a470f1801 100644 (file)
@@ -20,11 +20,12 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
   action_fallback(:errors)
 
   def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
-    new_info = [need_confirmation: false]
-
-    with %User{info: info} = user <- User.get_cached_by_id(uid),
-         true <- user.local and info.confirmation_pending and info.confirmation_token == token,
-         {:ok, _} <- User.update_info(user, &User.Info.confirmation_changeset(&1, new_info)) do
+    with %User{} = user <- User.get_cached_by_id(uid),
+         true <- user.local and user.confirmation_pending and user.confirmation_token == token,
+         {:ok, _} <-
+           user
+           |> User.confirmation_changeset(need_confirmation: false)
+           |> User.update_and_set_cache() do
       redirect(conn, to: "/")
     end
   end
index 21b086d4c635a2c47007a89191b78ca48a2ed8cd..c39b7f0951c4d9c8755a323811a1e9804a434bc6 100644 (file)
@@ -61,12 +61,12 @@ defmodule Pleroma.Web.MastoFEView do
       },
       poll_limits: Config.get([:instance, :poll_limits]),
       rights: %{
-        delete_others_notice: present?(user.info.is_moderator),
-        admin: present?(user.info.is_admin)
+        delete_others_notice: present?(user.is_moderator),
+        admin: present?(user.is_admin)
       },
       compose: %{
         me: "#{user.id}",
-        default_privacy: user.info.default_scope,
+        default_privacy: user.default_scope,
         default_sensitive: false,
         allow_content_types: Config.get([:instance, :allowed_post_formats])
       },
@@ -86,7 +86,7 @@ defmodule Pleroma.Web.MastoFEView do
           "video\/mp4"
         ]
       },
-      settings: user.info.settings || @default_settings,
+      settings: user.settings || @default_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),
@@ -99,4 +99,23 @@ defmodule Pleroma.Web.MastoFEView do
   defp present?(nil), do: false
   defp present?(false), do: false
   defp present?(_), do: true
+
+  def render("manifest.json", _params) do
+    %{
+      name: Config.get([:instance, :name]),
+      description: Config.get([:instance, :description]),
+      icons: Config.get([:manifest, :icons]),
+      theme_color: Config.get([:manifest, :theme_color]),
+      background_color: Config.get([:manifest, :background_color]),
+      display: "standalone",
+      scope: Pleroma.Web.base_url(),
+      start_url: masto_fe_path(Pleroma.Web.Endpoint, :index, ["getting-started"]),
+      categories: [
+        "social"
+      ],
+      serviceworker: %{
+        src: "/sw.js"
+      }
+    }
+  end
 end
index ecb39ee503b7d73c576b83118995316b228e49f2..b4cc801799e97a5fa3224875136bd0a645e98acb 100644 (file)
@@ -108,7 +108,6 @@ defmodule Pleroma.Web.WebFinger do
              doc
            ),
          subject <- XML.string_from_xpath("//Subject", doc),
-         salmon <- XML.string_from_xpath(~s{//Link[@rel="salmon"]/@href}, doc),
          subscribe_address <-
            XML.string_from_xpath(
              ~s{//Link[@rel="http://ostatus.org/schema/1.0/subscribe"]/@template},
@@ -123,7 +122,6 @@ defmodule Pleroma.Web.WebFinger do
         "magic_key" => magic_key,
         "topic" => topic,
         "subject" => subject,
-        "salmon" => salmon,
         "subscribe_address" => subscribe_address,
         "ap_id" => ap_id
       }
@@ -148,16 +146,6 @@ defmodule Pleroma.Web.WebFinger do
           {"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "self"} ->
             Map.put(data, "ap_id", link["href"])
 
-          {_, "magic-public-key"} ->
-            "data:application/magic-public-key," <> magic_key = link["href"]
-            Map.put(data, "magic_key", magic_key)
-
-          {"application/atom+xml", "http://schemas.google.com/g/2010#updates-from"} ->
-            Map.put(data, "topic", link["href"])
-
-          {_, "salmon"} ->
-            Map.put(data, "salmon", link["href"])
-
           {_, "http://ostatus.org/schema/1.0/subscribe"} ->
             Map.put(data, "subscribe_address", link["template"])
 
diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex
deleted file mode 100644 (file)
index b61f388..0000000
+++ /dev/null
@@ -1,332 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Websub do
-  alias Ecto.Changeset
-  alias Pleroma.Activity
-  alias Pleroma.HTTP
-  alias Pleroma.Instances
-  alias Pleroma.Repo
-  alias Pleroma.User
-  alias Pleroma.Web.ActivityPub.Visibility
-  alias Pleroma.Web.Endpoint
-  alias Pleroma.Web.Federator
-  alias Pleroma.Web.Federator.Publisher
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.OStatus.FeedRepresenter
-  alias Pleroma.Web.Router.Helpers
-  alias Pleroma.Web.Websub.WebsubClientSubscription
-  alias Pleroma.Web.Websub.WebsubServerSubscription
-  alias Pleroma.Web.XML
-  require Logger
-
-  import Ecto.Query
-
-  @behaviour Pleroma.Web.Federator.Publisher
-
-  def verify(subscription, getter \\ &HTTP.get/3) do
-    challenge = Base.encode16(:crypto.strong_rand_bytes(8))
-    lease_seconds = NaiveDateTime.diff(subscription.valid_until, subscription.updated_at)
-    lease_seconds = lease_seconds |> to_string
-
-    params = %{
-      "hub.challenge": challenge,
-      "hub.lease_seconds": lease_seconds,
-      "hub.topic": subscription.topic,
-      "hub.mode": "subscribe"
-    }
-
-    url = hd(String.split(subscription.callback, "?"))
-    query = URI.parse(subscription.callback).query || ""
-    params = Map.merge(params, URI.decode_query(query))
-
-    with {:ok, response} <- getter.(url, [], params: params),
-         ^challenge <- response.body do
-      changeset = Changeset.change(subscription, %{state: "active"})
-      Repo.update(changeset)
-    else
-      e ->
-        Logger.debug("Couldn't verify subscription")
-        Logger.debug(inspect(e))
-        {:error, subscription}
-    end
-  end
-
-  @supported_activities [
-    "Create",
-    "Follow",
-    "Like",
-    "Announce",
-    "Undo",
-    "Delete"
-  ]
-
-  def is_representable?(%Activity{data: %{"type" => type}} = activity)
-      when type in @supported_activities,
-      do: Visibility.is_public?(activity)
-
-  def is_representable?(_), do: false
-
-  def publish(topic, user, %{data: %{"type" => type}} = activity)
-      when type in @supported_activities do
-    response =
-      user
-      |> FeedRepresenter.to_simple_form([activity], [user])
-      |> :xmerl.export_simple(:xmerl_xml)
-      |> to_string
-
-    query =
-      from(
-        sub in WebsubServerSubscription,
-        where: sub.topic == ^topic and sub.state == "active",
-        where: fragment("? > (NOW() at time zone 'UTC')", sub.valid_until)
-      )
-
-    subscriptions = Repo.all(query)
-
-    callbacks = Enum.map(subscriptions, & &1.callback)
-    reachable_callbacks_metadata = Instances.filter_reachable(callbacks)
-    reachable_callbacks = Map.keys(reachable_callbacks_metadata)
-
-    subscriptions
-    |> Enum.filter(&(&1.callback in reachable_callbacks))
-    |> Enum.each(fn sub ->
-      data = %{
-        xml: response,
-        topic: topic,
-        callback: sub.callback,
-        secret: sub.secret,
-        unreachable_since: reachable_callbacks_metadata[sub.callback]
-      }
-
-      Publisher.enqueue_one(__MODULE__, data)
-    end)
-  end
-
-  def publish(_, _, _), do: ""
-
-  def publish(actor, activity), do: publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity)
-
-  def sign(secret, doc) do
-    :crypto.hmac(:sha, secret, to_string(doc)) |> Base.encode16() |> String.downcase()
-  end
-
-  def incoming_subscription_request(user, %{"hub.mode" => "subscribe"} = params) do
-    with {:ok, topic} <- valid_topic(params, user),
-         {:ok, lease_time} <- lease_time(params),
-         secret <- params["hub.secret"],
-         callback <- params["hub.callback"] do
-      subscription = get_subscription(topic, callback)
-
-      data = %{
-        state: subscription.state || "requested",
-        topic: topic,
-        secret: secret,
-        callback: callback
-      }
-
-      change = Changeset.change(subscription, data)
-      websub = Repo.insert_or_update!(change)
-
-      change =
-        Changeset.change(websub, %{valid_until: NaiveDateTime.add(websub.updated_at, lease_time)})
-
-      websub = Repo.update!(change)
-
-      Federator.verify_websub(websub)
-
-      {:ok, websub}
-    else
-      {:error, reason} ->
-        Logger.debug("Couldn't create subscription")
-        Logger.debug(inspect(reason))
-
-        {:error, reason}
-    end
-  end
-
-  def incoming_subscription_request(user, params) do
-    Logger.info("Unhandled WebSub request for #{user.nickname}: #{inspect(params)}")
-
-    {:error, "Invalid WebSub request"}
-  end
-
-  defp get_subscription(topic, callback) do
-    Repo.get_by(WebsubServerSubscription, topic: topic, callback: callback) ||
-      %WebsubServerSubscription{}
-  end
-
-  # Temp hack for mastodon.
-  defp lease_time(%{"hub.lease_seconds" => ""}) do
-    # three days
-    {:ok, 60 * 60 * 24 * 3}
-  end
-
-  defp lease_time(%{"hub.lease_seconds" => lease_seconds}) do
-    {:ok, String.to_integer(lease_seconds)}
-  end
-
-  defp lease_time(_) do
-    # three days
-    {:ok, 60 * 60 * 24 * 3}
-  end
-
-  defp valid_topic(%{"hub.topic" => topic}, user) do
-    if topic == OStatus.feed_path(user) do
-      {:ok, OStatus.feed_path(user)}
-    else
-      {:error, "Wrong topic requested, expected #{OStatus.feed_path(user)}, got #{topic}"}
-    end
-  end
-
-  def subscribe(subscriber, subscribed, requester \\ &request_subscription/1) do
-    topic = subscribed.info.topic
-    # FIXME: Race condition, use transactions
-    {:ok, subscription} =
-      with subscription when not is_nil(subscription) <-
-             Repo.get_by(WebsubClientSubscription, topic: topic) do
-        subscribers = [subscriber.ap_id | subscription.subscribers] |> Enum.uniq()
-        change = Ecto.Changeset.change(subscription, %{subscribers: subscribers})
-        Repo.update(change)
-      else
-        _e ->
-          subscription = %WebsubClientSubscription{
-            topic: topic,
-            hub: subscribed.info.hub,
-            subscribers: [subscriber.ap_id],
-            state: "requested",
-            secret: :crypto.strong_rand_bytes(8) |> Base.url_encode64(),
-            user: subscribed
-          }
-
-          Repo.insert(subscription)
-      end
-
-    requester.(subscription)
-  end
-
-  def gather_feed_data(topic, getter \\ &HTTP.get/1) do
-    with {:ok, response} <- getter.(topic),
-         status when status in 200..299 <- response.status,
-         body <- response.body,
-         doc <- XML.parse_document(body),
-         uri when not is_nil(uri) <- XML.string_from_xpath("/feed/author[1]/uri", doc),
-         hub when not is_nil(hub) <- XML.string_from_xpath(~S{/feed/link[@rel="hub"]/@href}, doc) do
-      name = XML.string_from_xpath("/feed/author[1]/name", doc)
-      preferred_username = XML.string_from_xpath("/feed/author[1]/poco:preferredUsername", doc)
-      display_name = XML.string_from_xpath("/feed/author[1]/poco:displayName", doc)
-      avatar = OStatus.make_avatar_object(doc)
-      bio = XML.string_from_xpath("/feed/author[1]/summary", doc)
-
-      {:ok,
-       %{
-         "uri" => uri,
-         "hub" => hub,
-         "nickname" => preferred_username || name,
-         "name" => display_name || name,
-         "host" => URI.parse(uri).host,
-         "avatar" => avatar,
-         "bio" => bio
-       }}
-    else
-      e ->
-        {:error, e}
-    end
-  end
-
-  def request_subscription(websub, poster \\ &HTTP.post/3, timeout \\ 10_000) do
-    data = [
-      "hub.mode": "subscribe",
-      "hub.topic": websub.topic,
-      "hub.secret": websub.secret,
-      "hub.callback": Helpers.websub_url(Endpoint, :websub_subscription_confirmation, websub.id)
-    ]
-
-    # This checks once a second if we are confirmed yet
-    websub_checker = fn ->
-      helper = fn helper ->
-        :timer.sleep(1000)
-        websub = Repo.get_by(WebsubClientSubscription, id: websub.id, state: "accepted")
-        if websub, do: websub, else: helper.(helper)
-      end
-
-      helper.(helper)
-    end
-
-    task = Task.async(websub_checker)
-
-    with {:ok, %{status: 202}} <-
-           poster.(websub.hub, {:form, data}, "Content-type": "application/x-www-form-urlencoded"),
-         {:ok, websub} <- Task.yield(task, timeout) do
-      {:ok, websub}
-    else
-      e ->
-        Task.shutdown(task)
-
-        change = Ecto.Changeset.change(websub, %{state: "rejected"})
-        {:ok, websub} = Repo.update(change)
-
-        Logger.debug(fn -> "Couldn't confirm subscription: #{inspect(websub)}" end)
-        Logger.debug(fn -> "error: #{inspect(e)}" end)
-
-        {:error, websub}
-    end
-  end
-
-  def refresh_subscriptions(delta \\ 60 * 60 * 24) do
-    Logger.debug("Refreshing subscriptions")
-
-    cut_off = NaiveDateTime.add(NaiveDateTime.utc_now(), delta)
-
-    query = from(sub in WebsubClientSubscription, where: sub.valid_until < ^cut_off)
-
-    subs = Repo.all(query)
-
-    Enum.each(subs, fn sub ->
-      Federator.request_subscription(sub)
-    end)
-  end
-
-  def publish_one(%{xml: xml, topic: topic, callback: callback, secret: secret} = params) do
-    signature = sign(secret || "", xml)
-    Logger.info(fn -> "Pushing #{topic} to #{callback}" end)
-
-    with {:ok, %{status: code}} when code in 200..299 <-
-           HTTP.post(
-             callback,
-             xml,
-             [
-               {"Content-Type", "application/atom+xml"},
-               {"X-Hub-Signature", "sha1=#{signature}"}
-             ]
-           ) do
-      if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since],
-        do: Instances.set_reachable(callback)
-
-      Logger.info(fn -> "Pushed to #{callback}, code #{code}" end)
-      {:ok, code}
-    else
-      {_post_result, response} ->
-        unless params[:unreachable_since], do: Instances.set_reachable(callback)
-        Logger.debug(fn -> "Couldn't push to #{callback}, #{inspect(response)}" end)
-        {:error, response}
-    end
-  end
-
-  def gather_webfinger_links(%User{} = user) do
-    [
-      %{
-        "rel" => "http://schemas.google.com/g/2010#updates-from",
-        "type" => "application/atom+xml",
-        "href" => OStatus.feed_path(user)
-      },
-      %{
-        "rel" => "http://ostatus.org/schema/1.0/subscribe",
-        "template" => OStatus.remote_follow_path()
-      }
-    ]
-  end
-
-  def gather_nodeinfo_protocol_names, do: ["ostatus"]
-end
diff --git a/lib/pleroma/web/websub/websub_client_subscription.ex b/lib/pleroma/web/websub/websub_client_subscription.ex
deleted file mode 100644 (file)
index 23a04b8..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Websub.WebsubClientSubscription do
-  use Ecto.Schema
-  alias Pleroma.User
-
-  schema "websub_client_subscriptions" do
-    field(:topic, :string)
-    field(:secret, :string)
-    field(:valid_until, :naive_datetime_usec)
-    field(:state, :string)
-    field(:subscribers, {:array, :string}, default: [])
-    field(:hub, :string)
-    belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
-
-    timestamps()
-  end
-end
diff --git a/lib/pleroma/web/websub/websub_controller.ex b/lib/pleroma/web/websub/websub_controller.ex
deleted file mode 100644 (file)
index 9e8b48b..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Websub.WebsubController do
-  use Pleroma.Web, :controller
-
-  alias Pleroma.Repo
-  alias Pleroma.User
-  alias Pleroma.Web.Federator
-  alias Pleroma.Web.Websub
-  alias Pleroma.Web.Websub.WebsubClientSubscription
-
-  require Logger
-
-  plug(
-    Pleroma.Web.FederatingPlug
-    when action in [
-           :websub_subscription_request,
-           :websub_subscription_confirmation,
-           :websub_incoming
-         ]
-  )
-
-  def websub_subscription_request(conn, %{"nickname" => nickname} = params) do
-    user = User.get_cached_by_nickname(nickname)
-
-    with {:ok, _websub} <- Websub.incoming_subscription_request(user, params) do
-      conn
-      |> send_resp(202, "Accepted")
-    else
-      {:error, reason} ->
-        conn
-        |> send_resp(500, reason)
-    end
-  end
-
-  # TODO: Extract this into the Websub module
-  def websub_subscription_confirmation(
-        conn,
-        %{
-          "id" => id,
-          "hub.mode" => "subscribe",
-          "hub.challenge" => challenge,
-          "hub.topic" => topic
-        } = params
-      ) do
-    Logger.debug("Got WebSub confirmation")
-    Logger.debug(inspect(params))
-
-    lease_seconds =
-      if params["hub.lease_seconds"] do
-        String.to_integer(params["hub.lease_seconds"])
-      else
-        # Guess 3 days
-        60 * 60 * 24 * 3
-      end
-
-    with %WebsubClientSubscription{} = websub <-
-           Repo.get_by(WebsubClientSubscription, id: id, topic: topic) do
-      valid_until = NaiveDateTime.add(NaiveDateTime.utc_now(), lease_seconds)
-      change = Ecto.Changeset.change(websub, %{state: "accepted", valid_until: valid_until})
-      {:ok, _websub} = Repo.update(change)
-
-      conn
-      |> send_resp(200, challenge)
-    else
-      _e ->
-        conn
-        |> send_resp(500, "Error")
-    end
-  end
-
-  def websub_subscription_confirmation(conn, params) do
-    Logger.info("Invalid WebSub confirmation request: #{inspect(params)}")
-
-    conn
-    |> send_resp(500, "Invalid parameters")
-  end
-
-  def websub_incoming(conn, %{"id" => id}) do
-    with "sha1=" <> signature <- hd(get_req_header(conn, "x-hub-signature")),
-         signature <- String.downcase(signature),
-         %WebsubClientSubscription{} = websub <- Repo.get(WebsubClientSubscription, id),
-         {:ok, body, _conn} = read_body(conn),
-         ^signature <- Websub.sign(websub.secret, body) do
-      Federator.incoming_doc(body)
-
-      conn
-      |> send_resp(200, "OK")
-    else
-      _e ->
-        Logger.debug("Can't handle incoming subscription post")
-
-        conn
-        |> send_resp(500, "Error")
-    end
-  end
-end
diff --git a/lib/pleroma/web/websub/websub_server_subscription.ex b/lib/pleroma/web/websub/websub_server_subscription.ex
deleted file mode 100644 (file)
index d0ef548..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Websub.WebsubServerSubscription do
-  use Ecto.Schema
-
-  schema "websub_server_subscriptions" do
-    field(:topic, :string)
-    field(:callback, :string)
-    field(:secret, :string)
-    field(:valid_until, :naive_datetime)
-    field(:state, :string)
-
-    timestamps()
-  end
-end
index 83d528a660f83d69dbba201366753c82f2318991..8ad756b625ec0c82d5a681b653f699d9f9d88ab6 100644 (file)
@@ -8,10 +8,6 @@ defmodule Pleroma.Workers.ReceiverWorker do
   use Pleroma.Workers.WorkerHelper, queue: "federator_incoming"
 
   @impl Oban.Worker
-  def perform(%{"op" => "incoming_doc", "body" => doc}, _job) do
-    Federator.perform(:incoming_doc, doc)
-  end
-
   def perform(%{"op" => "incoming_ap_doc", "params" => params}, _job) do
     Federator.perform(:incoming_ap_doc, params)
   end
diff --git a/lib/pleroma/workers/subscriber_worker.ex b/lib/pleroma/workers/subscriber_worker.ex
deleted file mode 100644 (file)
index fc490e3..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Workers.SubscriberWorker do
-  alias Pleroma.Repo
-  alias Pleroma.Web.Federator
-  alias Pleroma.Web.Websub
-
-  use Pleroma.Workers.WorkerHelper, queue: "federator_outgoing"
-
-  @impl Oban.Worker
-  def perform(%{"op" => "refresh_subscriptions"}, _job) do
-    Federator.perform(:refresh_subscriptions)
-  end
-
-  def perform(%{"op" => "request_subscription", "websub_id" => websub_id}, _job) do
-    websub = Repo.get(Websub.WebsubClientSubscription, websub_id)
-    Federator.perform(:request_subscription, websub)
-  end
-
-  def perform(%{"op" => "verify_websub", "websub_id" => websub_id}, _job) do
-    websub = Repo.get(Websub.WebsubServerSubscription, websub_id)
-    Federator.perform(:verify_websub, websub)
-  end
-end
diff --git a/mix.exs b/mix.exs
index 6cf766c521cdbd109387b2b471b58b5c07c17397..dcb9d9ea8ff10598ffbddbe4be2b1119575df29f 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
   def project do
     [
       app: :pleroma,
-      version: version("1.0.0"),
+      version: version("1.1.50"),
       elixir: "~> 1.8",
       elixirc_paths: elixirc_paths(Mix.env()),
       compilers: [:phoenix, :gettext] ++ Mix.compilers(),
@@ -69,6 +69,7 @@ defmodule Pleroma.Mixfile do
   end
 
   # Specifies which paths to compile per environment.
+  defp elixirc_paths(:benchmark), do: ["lib", "benchmarks"]
   defp elixirc_paths(:test), do: ["lib", "test/support"]
   defp elixirc_paths(_), do: ["lib"]
 
index 779aa382e903d6081a8c293d864928cbd6e8d79b..a5170d5215e801ccbbd99a4036eb4c545f97e3b3 100644 (file)
@@ -5,7 +5,11 @@ defmodule Pleroma.Repo.Migrations.AddFollowingAddressFromSourceData do
 
   def change do
     query =
-      User.external_users_query()
+      User.Query.build(%{
+        external: true,
+        legacy_active: true,
+        order_by: :id
+      })
       |> select([u], struct(u, [:id, :ap_id, :info]))
 
     Pleroma.Repo.stream(query)
diff --git a/priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs b/priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs
new file mode 100644 (file)
index 0000000..2f336a5
--- /dev/null
@@ -0,0 +1,22 @@
+defmodule Pleroma.Repo.Migrations.CreateSafeJsonbSet do
+  use Ecto.Migration
+  alias Pleroma.User
+
+  def change do
+    execute("""
+    create or replace function safe_jsonb_set(target jsonb, path text[], new_value jsonb, create_missing boolean default true) returns jsonb as $$
+    declare
+      result jsonb;
+    begin
+      result := jsonb_set(target, path, coalesce(new_value, 'null'::jsonb), create_missing);
+      if result is NULL then
+        raise 'jsonb_set tried to wipe the object, please report this incindent to Pleroma bug tracker. https://git.pleroma.social/pleroma/pleroma/issues/new';
+        return target;
+      else
+        return result;
+      end if;
+    end;
+    $$ language plpgsql;
+    """)
+  end
+end
index bc4e828cc4a95253c9f7d962276ee0cd0d76a4cc..fc9bf70baf477ac311a6a7de488d67a15e0ab8d6 100644 (file)
@@ -1,10 +1,9 @@
 defmodule Pleroma.Repo.Migrations.CopyMutedToMutedNotifications do
   use Ecto.Migration
-  alias Pleroma.User
 
   def change do
     execute(
-      "update users set info = jsonb_set(info, '{muted_notifications}', info->'mutes', true) where local = true"
+      "update users set info = safe_jsonb_set(info, '{muted_notifications}', info->'mutes', true) where local = true"
     )
   end
 end
diff --git a/priv/repo/migrations/20191009154606_add_user_info_columns.exs b/priv/repo/migrations/20191009154606_add_user_info_columns.exs
new file mode 100644 (file)
index 0000000..22a5a37
--- /dev/null
@@ -0,0 +1,53 @@
+defmodule Pleroma.Repo.Migrations.AddUsersInfoColumns do
+  use Ecto.Migration
+
+  @jsonb_array_default "'[]'::jsonb"
+
+  def change do
+    alter table(:users) do
+      add_if_not_exists(:banner, :map, default: %{})
+      add_if_not_exists(:background, :map, default: %{})
+      add_if_not_exists(:source_data, :map, default: %{})
+      add_if_not_exists(:note_count, :integer, default: 0)
+      add_if_not_exists(:follower_count, :integer, default: 0)
+      add_if_not_exists(:following_count, :integer, default: nil)
+      add_if_not_exists(:locked, :boolean, default: false, null: false)
+      add_if_not_exists(:confirmation_pending, :boolean, default: false, null: false)
+      add_if_not_exists(:password_reset_pending, :boolean, default: false, null: false)
+      add_if_not_exists(:confirmation_token, :text, default: nil)
+      add_if_not_exists(:default_scope, :string, default: "public")
+      add_if_not_exists(:blocks, {:array, :text}, default: [])
+      add_if_not_exists(:domain_blocks, {:array, :text}, default: [])
+      add_if_not_exists(:mutes, {:array, :text}, default: [])
+      add_if_not_exists(:muted_reblogs, {:array, :text}, default: [])
+      add_if_not_exists(:muted_notifications, {:array, :text}, default: [])
+      add_if_not_exists(:subscribers, {:array, :text}, default: [])
+      add_if_not_exists(:deactivated, :boolean, default: false, null: false)
+      add_if_not_exists(:no_rich_text, :boolean, default: false, null: false)
+      add_if_not_exists(:ap_enabled, :boolean, default: false, null: false)
+      add_if_not_exists(:is_moderator, :boolean, default: false, null: false)
+      add_if_not_exists(:is_admin, :boolean, default: false, null: false)
+      add_if_not_exists(:show_role, :boolean, default: true, null: false)
+      add_if_not_exists(:settings, :map, default: nil)
+      add_if_not_exists(:magic_key, :text, default: nil)
+      add_if_not_exists(:uri, :text, default: nil)
+      add_if_not_exists(:hide_followers_count, :boolean, default: false, null: false)
+      add_if_not_exists(:hide_follows_count, :boolean, default: false, null: false)
+      add_if_not_exists(:hide_followers, :boolean, default: false, null: false)
+      add_if_not_exists(:hide_follows, :boolean, default: false, null: false)
+      add_if_not_exists(:hide_favorites, :boolean, default: true, null: false)
+      add_if_not_exists(:unread_conversation_count, :integer, default: 0)
+      add_if_not_exists(:pinned_activities, {:array, :text}, default: [])
+      add_if_not_exists(:email_notifications, :map, default: %{"digest" => false})
+      add_if_not_exists(:mascot, :map, default: nil)
+      add_if_not_exists(:emoji, :map, default: fragment(@jsonb_array_default))
+      add_if_not_exists(:pleroma_settings_store, :map, default: %{})
+      add_if_not_exists(:fields, :map, default: fragment(@jsonb_array_default))
+      add_if_not_exists(:raw_fields, :map, default: fragment(@jsonb_array_default))
+      add_if_not_exists(:discoverable, :boolean, default: false, null: false)
+      add_if_not_exists(:invisible, :boolean, default: false, null: false)
+      add_if_not_exists(:notification_settings, :map, default: %{})
+      add_if_not_exists(:skip_thread_containment, :boolean, default: false, null: false)
+    end
+  end
+end
diff --git a/priv/repo/migrations/20191009154608_copy_users_info_fields_to_users.exs b/priv/repo/migrations/20191009154608_copy_users_info_fields_to_users.exs
new file mode 100644 (file)
index 0000000..cc5ae69
--- /dev/null
@@ -0,0 +1,145 @@
+defmodule Pleroma.Repo.Migrations.CopyUsersInfoFieldsToUsers do
+  use Ecto.Migration
+
+  @jsonb_array_default "'[]'::jsonb"
+
+  @info_fields [
+    :banner,
+    :background,
+    :source_data,
+    :note_count,
+    :follower_count,
+    :following_count,
+    :locked,
+    :confirmation_pending,
+    :password_reset_pending,
+    :confirmation_token,
+    :default_scope,
+    :blocks,
+    :domain_blocks,
+    :mutes,
+    :muted_reblogs,
+    :muted_notifications,
+    :subscribers,
+    :deactivated,
+    :no_rich_text,
+    :ap_enabled,
+    :is_moderator,
+    :is_admin,
+    :show_role,
+    :settings,
+    :magic_key,
+    :uri,
+    :hide_followers_count,
+    :hide_follows_count,
+    :hide_followers,
+    :hide_follows,
+    :hide_favorites,
+    :unread_conversation_count,
+    :pinned_activities,
+    :email_notifications,
+    :mascot,
+    :emoji,
+    :pleroma_settings_store,
+    :fields,
+    :raw_fields,
+    :discoverable,
+    :invisible,
+    :skip_thread_containment,
+    :notification_settings
+  ]
+
+  @jsonb_fields [
+    :banner,
+    :background,
+    :source_data,
+    :settings,
+    :email_notifications,
+    :mascot,
+    :pleroma_settings_store,
+    :notification_settings
+  ]
+
+  @array_jsonb_fields [:emoji, :fields, :raw_fields]
+
+  @int_fields [:note_count, :follower_count, :following_count, :unread_conversation_count]
+
+  @boolean_fields [
+    :locked,
+    :confirmation_pending,
+    :password_reset_pending,
+    :deactivated,
+    :no_rich_text,
+    :ap_enabled,
+    :is_moderator,
+    :is_admin,
+    :show_role,
+    :hide_followers_count,
+    :hide_follows_count,
+    :hide_followers,
+    :hide_follows,
+    :hide_favorites,
+    :discoverable,
+    :invisible,
+    :skip_thread_containment
+  ]
+
+  @array_text_fields [
+    :blocks,
+    :domain_blocks,
+    :mutes,
+    :muted_reblogs,
+    :muted_notifications,
+    :subscribers,
+    :pinned_activities
+  ]
+
+  def change do
+    if direction() == :up do
+      sets =
+        for f <- @info_fields do
+          set_field = "#{f} ="
+
+          # Coercion of null::jsonb to NULL
+          jsonb = "case when info->>'#{f}' IS NULL then null else info->'#{f}' end"
+
+          cond do
+            f in @jsonb_fields ->
+              "#{set_field} #{jsonb}"
+
+            f in @array_jsonb_fields ->
+              "#{set_field} coalesce(#{jsonb}, #{@jsonb_array_default})"
+
+            f in @int_fields ->
+              "#{set_field} (info->>'#{f}')::int"
+
+            f in @boolean_fields ->
+              "#{set_field} coalesce((info->>'#{f}')::boolean, false)"
+
+            f in @array_text_fields ->
+              "#{set_field} ARRAY(SELECT jsonb_array_elements_text(#{jsonb}))"
+
+            true ->
+              "#{set_field} info->>'#{f}'"
+          end
+        end
+        |> Enum.join(", ")
+
+      execute("update users set " <> sets)
+
+      for index_name <- [
+            :users_deactivated_index,
+            :users_is_moderator_index,
+            :users_is_admin_index,
+            :users_subscribers_index
+          ] do
+        drop_if_exists(index(:users, [], name: index_name))
+      end
+    end
+
+    create_if_not_exists(index(:users, [:deactivated]))
+    create_if_not_exists(index(:users, [:is_moderator]))
+    create_if_not_exists(index(:users, [:is_admin]))
+    create_if_not_exists(index(:users, [:subscribers]))
+  end
+end
diff --git a/priv/repo/migrations/20191014181019_create_markers.exs b/priv/repo/migrations/20191014181019_create_markers.exs
new file mode 100644 (file)
index 0000000..c717831
--- /dev/null
@@ -0,0 +1,15 @@
+defmodule Pleroma.Repo.Migrations.CreateMarkers do
+  use Ecto.Migration
+
+  def change do
+    create_if_not_exists table(:markers) do
+      add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
+      add(:timeline, :string, default: "", null: false)
+      add(:last_read_id, :string, default: "", null: false)
+      add(:lock_version, :integer, default: 0, null: false)
+      timestamps()
+    end
+
+    create_if_not_exists(unique_index(:markers, [:user_id, :timeline]))
+  end
+end
diff --git a/priv/repo/migrations/20191017225002_drop_websub_tables.exs b/priv/repo/migrations/20191017225002_drop_websub_tables.exs
new file mode 100644 (file)
index 0000000..4cf67a5
--- /dev/null
@@ -0,0 +1,10 @@
+defmodule Pleroma.Repo.Migrations.DropWebsubTables do
+  use Ecto.Migration
+
+  def up do
+    drop_if_exists(table(:websub_client_subscriptions))
+    drop_if_exists(table(:websub_server_subscriptions))
+  end
+
+  def down, do: :noop
+end
diff --git a/priv/repo/migrations/20191026190317_set_not_null_for_activities.exs b/priv/repo/migrations/20191026190317_set_not_null_for_activities.exs
new file mode 100644 (file)
index 0000000..9b66f3c
--- /dev/null
@@ -0,0 +1,17 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForActivities do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE activities
+    ALTER COLUMN data SET NOT NULL,
+    ALTER COLUMN local SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE activities
+    ALTER COLUMN data DROP NOT NULL,
+    ALTER COLUMN local DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026190415_set_not_null_for_activity_expirations.exs b/priv/repo/migrations/20191026190415_set_not_null_for_activity_expirations.exs
new file mode 100644 (file)
index 0000000..e41c69e
--- /dev/null
@@ -0,0 +1,15 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForActivityExpirations do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE activity_expirations
+    ALTER COLUMN activity_id SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE activity_expirations
+    ALTER COLUMN activity_id DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026190500_set_not_null_for_apps.exs b/priv/repo/migrations/20191026190500_set_not_null_for_apps.exs
new file mode 100644 (file)
index 0000000..a6a44dd
--- /dev/null
@@ -0,0 +1,17 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForApps do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE apps
+    ALTER COLUMN client_name SET NOT NULL,
+    ALTER COLUMN redirect_uris SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE apps
+    ALTER COLUMN client_name DROP NOT NULL,
+    ALTER COLUMN redirect_uris DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026190533_set_not_null_for_bookmarks.exs b/priv/repo/migrations/20191026190533_set_not_null_for_bookmarks.exs
new file mode 100644 (file)
index 0000000..5f32240
--- /dev/null
@@ -0,0 +1,17 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForBookmarks do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE bookmarks
+    ALTER COLUMN user_id SET NOT NULL,
+    ALTER COLUMN activity_id SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE bookmarks
+    ALTER COLUMN user_id DROP NOT NULL,
+    ALTER COLUMN activity_id DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026190622_set_not_null_for_config.exs b/priv/repo/migrations/20191026190622_set_not_null_for_config.exs
new file mode 100644 (file)
index 0000000..2042724
--- /dev/null
@@ -0,0 +1,17 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForConfig do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE config
+    ALTER COLUMN key SET NOT NULL,
+    ALTER COLUMN value SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE config
+    ALTER COLUMN key DROP NOT NULL,
+    ALTER COLUMN value DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026190712_set_not_null_for_conversation_participation_recipient_ships.exs b/priv/repo/migrations/20191026190712_set_not_null_for_conversation_participation_recipient_ships.exs
new file mode 100644 (file)
index 0000000..a5ab1d3
--- /dev/null
@@ -0,0 +1,17 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForConversationParticipationRecipientShips do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE conversation_participation_recipient_ships
+    ALTER COLUMN user_id SET NOT NULL,
+    ALTER COLUMN participation_id SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE conversation_participation_recipient_ships
+    ALTER COLUMN user_id DROP NOT NULL,
+    ALTER COLUMN participation_id DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026190759_set_not_null_for_conversation_participations.exs b/priv/repo/migrations/20191026190759_set_not_null_for_conversation_participations.exs
new file mode 100644 (file)
index 0000000..cabb1f2
--- /dev/null
@@ -0,0 +1,19 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForConversationParticipations do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE conversation_participations
+    ALTER COLUMN user_id SET NOT NULL,
+    ALTER COLUMN conversation_id SET NOT NULL,
+    ALTER COLUMN read SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE conversation_participations
+    ALTER COLUMN user_id DROP NOT NULL,
+    ALTER COLUMN conversation_id DROP NOT NULL,
+    ALTER COLUMN read DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026190841_set_not_null_for_filters.exs b/priv/repo/migrations/20191026190841_set_not_null_for_filters.exs
new file mode 100644 (file)
index 0000000..52d7e68
--- /dev/null
@@ -0,0 +1,19 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForFilters do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE filters
+    ALTER COLUMN user_id SET NOT NULL,
+    ALTER COLUMN filter_id SET NOT NULL,
+    ALTER COLUMN whole_word SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE filters
+    ALTER COLUMN user_id DROP NOT NULL,
+    ALTER COLUMN filter_id DROP NOT NULL,
+    ALTER COLUMN whole_word DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026191023_set_not_null_for_instances.exs b/priv/repo/migrations/20191026191023_set_not_null_for_instances.exs
new file mode 100644 (file)
index 0000000..4c2560d
--- /dev/null
@@ -0,0 +1,15 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForInstances do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE instances
+    ALTER COLUMN host SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE instances
+    ALTER COLUMN host DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026191100_set_not_null_for_lists.exs b/priv/repo/migrations/20191026191100_set_not_null_for_lists.exs
new file mode 100644 (file)
index 0000000..40b8b13
--- /dev/null
@@ -0,0 +1,15 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForLists do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE lists
+    ALTER COLUMN user_id SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE lists
+    ALTER COLUMN user_id DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026191134_set_not_null_for_markers.exs b/priv/repo/migrations/20191026191134_set_not_null_for_markers.exs
new file mode 100644 (file)
index 0000000..7d7b73e
--- /dev/null
@@ -0,0 +1,15 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForMarkers do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE markers
+    ALTER COLUMN user_id SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE markers
+    ALTER COLUMN user_id DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026191218_set_not_null_for_moderation_log.exs b/priv/repo/migrations/20191026191218_set_not_null_for_moderation_log.exs
new file mode 100644 (file)
index 0000000..7238ca7
--- /dev/null
@@ -0,0 +1,15 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForModerationLog do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE moderation_log
+    ALTER COLUMN data SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE moderation_log
+    ALTER COLUMN data DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026191249_set_not_null_for_notifications.exs b/priv/repo/migrations/20191026191249_set_not_null_for_notifications.exs
new file mode 100644 (file)
index 0000000..7e2976b
--- /dev/null
@@ -0,0 +1,17 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForNotifications do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE notifications
+    ALTER COLUMN user_id SET NOT NULL,
+    ALTER COLUMN seen SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE notifications
+    ALTER COLUMN user_id DROP NOT NULL,
+    ALTER COLUMN seen DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026191328_set_not_null_for_oauth_authorizations.exs b/priv/repo/migrations/20191026191328_set_not_null_for_oauth_authorizations.exs
new file mode 100644 (file)
index 0000000..bc6b36e
--- /dev/null
@@ -0,0 +1,19 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForOauthAuthorizations do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE oauth_authorizations
+    ALTER COLUMN app_id SET NOT NULL,
+    ALTER COLUMN token SET NOT NULL,
+    ALTER COLUMN used SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE oauth_authorizations
+    ALTER COLUMN app_id DROP NOT NULL,
+    ALTER COLUMN token DROP NOT NULL,
+    ALTER COLUMN used DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026191401_set_not_null_for_oauth_tokens.exs b/priv/repo/migrations/20191026191401_set_not_null_for_oauth_tokens.exs
new file mode 100644 (file)
index 0000000..fe67db8
--- /dev/null
@@ -0,0 +1,15 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForOauthTokens do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE oauth_tokens
+    ALTER COLUMN app_id SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE oauth_tokens
+    ALTER COLUMN app_id DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026191442_set_not_null_for_objects.exs b/priv/repo/migrations/20191026191442_set_not_null_for_objects.exs
new file mode 100644 (file)
index 0000000..59e89d6
--- /dev/null
@@ -0,0 +1,15 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForObjects do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE objects
+    ALTER COLUMN data SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE objects
+    ALTER COLUMN data DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026191524_set_not_null_for_password_reset_tokens.exs b/priv/repo/migrations/20191026191524_set_not_null_for_password_reset_tokens.exs
new file mode 100644 (file)
index 0000000..51efadb
--- /dev/null
@@ -0,0 +1,19 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForPasswordResetTokens do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE password_reset_tokens
+    ALTER COLUMN token SET NOT NULL,
+    ALTER COLUMN user_id SET NOT NULL,
+    ALTER COLUMN used SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE password_reset_tokens
+    ALTER COLUMN token DROP NOT NULL,
+    ALTER COLUMN user_id DROP NOT NULL,
+    ALTER COLUMN used DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026191603_set_not_null_for_push_subscriptions.exs b/priv/repo/migrations/20191026191603_set_not_null_for_push_subscriptions.exs
new file mode 100644 (file)
index 0000000..0f3a730
--- /dev/null
@@ -0,0 +1,25 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForPushSubscriptions do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE push_subscriptions
+    ALTER COLUMN user_id SET NOT NULL,
+    ALTER COLUMN token_id SET NOT NULL,
+    ALTER COLUMN endpoint SET NOT NULL,
+    ALTER COLUMN key_p256dh SET NOT NULL,
+    ALTER COLUMN key_auth SET NOT NULL,
+    ALTER COLUMN data SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE push_subscriptions
+    ALTER COLUMN user_id DROP NOT NULL,
+    ALTER COLUMN token_id DROP NOT NULL,
+    ALTER COLUMN endpoint DROP NOT NULL,
+    ALTER COLUMN key_p256dh DROP NOT NULL,
+    ALTER COLUMN key_auth DROP NOT NULL,
+    ALTER COLUMN data DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026191635_set_not_null_for_registrations.exs b/priv/repo/migrations/20191026191635_set_not_null_for_registrations.exs
new file mode 100644 (file)
index 0000000..ddfbf4c
--- /dev/null
@@ -0,0 +1,19 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForRegistrations do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE registrations
+    ALTER COLUMN provider SET NOT NULL,
+    ALTER COLUMN uid SET NOT NULL,
+    ALTER COLUMN info SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE registrations
+    ALTER COLUMN provider DROP NOT NULL,
+    ALTER COLUMN uid DROP NOT NULL,
+    ALTER COLUMN info DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026191711_set_not_null_for_scheduled_activities.exs b/priv/repo/migrations/20191026191711_set_not_null_for_scheduled_activities.exs
new file mode 100644 (file)
index 0000000..f1830c8
--- /dev/null
@@ -0,0 +1,15 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForScheduledActivities do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE scheduled_activities
+    ALTER COLUMN user_id SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE scheduled_activities
+    ALTER COLUMN user_id DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026191753_set_not_null_for_thread_mutes.exs b/priv/repo/migrations/20191026191753_set_not_null_for_thread_mutes.exs
new file mode 100644 (file)
index 0000000..daa7ce3
--- /dev/null
@@ -0,0 +1,17 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForThreadMutes do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE thread_mutes
+    ALTER COLUMN user_id SET NOT NULL,
+    ALTER COLUMN context SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE thread_mutes
+    ALTER COLUMN user_id DROP NOT NULL,
+    ALTER COLUMN context DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026191826_set_not_null_for_user_invite_tokens.exs b/priv/repo/migrations/20191026191826_set_not_null_for_user_invite_tokens.exs
new file mode 100644 (file)
index 0000000..836544f
--- /dev/null
@@ -0,0 +1,19 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForUserInviteTokens do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    execute("ALTER TABLE user_invite_tokens
+    ALTER COLUMN used SET NOT NULL,
+    ALTER COLUMN uses SET NOT NULL,
+    ALTER COLUMN invite_type SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE user_invite_tokens
+    ALTER COLUMN used DROP NOT NULL,
+    ALTER COLUMN uses DROP NOT NULL,
+    ALTER COLUMN invite_type DROP NOT NULL")
+  end
+end
diff --git a/priv/repo/migrations/20191026191910_set_not_null_for_users.exs b/priv/repo/migrations/20191026191910_set_not_null_for_users.exs
new file mode 100644 (file)
index 0000000..f145a89
--- /dev/null
@@ -0,0 +1,46 @@
+defmodule Pleroma.Repo.Migrations.SetNotNullForUsers do
+  use Ecto.Migration
+
+  # modify/3 function will require index recreation, so using execute/1 instead
+
+  def up do
+    # irreversible
+    execute("UPDATE users SET follower_count = 0 WHERE follower_count IS NULL")
+
+    execute("ALTER TABLE users
+    ALTER COLUMN following SET NOT NULL,
+    ALTER COLUMN local SET NOT NULL,
+    ALTER COLUMN source_data SET NOT NULL,
+    ALTER COLUMN note_count SET NOT NULL,
+    ALTER COLUMN follower_count SET NOT NULL,
+    ALTER COLUMN blocks SET NOT NULL,
+    ALTER COLUMN domain_blocks SET NOT NULL,
+    ALTER COLUMN mutes SET NOT NULL,
+    ALTER COLUMN muted_reblogs SET NOT NULL,
+    ALTER COLUMN muted_notifications SET NOT NULL,
+    ALTER COLUMN subscribers SET NOT NULL,
+    ALTER COLUMN pinned_activities SET NOT NULL,
+    ALTER COLUMN emoji SET NOT NULL,
+    ALTER COLUMN fields SET NOT NULL,
+    ALTER COLUMN raw_fields SET NOT NULL")
+  end
+
+  def down do
+    execute("ALTER TABLE users
+    ALTER COLUMN following DROP NOT NULL,
+    ALTER COLUMN local DROP NOT NULL,
+    ALTER COLUMN source_data DROP NOT NULL,
+    ALTER COLUMN note_count DROP NOT NULL,
+    ALTER COLUMN follower_count DROP NOT NULL,
+    ALTER COLUMN blocks DROP NOT NULL,
+    ALTER COLUMN domain_blocks DROP NOT NULL,
+    ALTER COLUMN mutes DROP NOT NULL,
+    ALTER COLUMN muted_reblogs DROP NOT NULL,
+    ALTER COLUMN muted_notifications DROP NOT NULL,
+    ALTER COLUMN subscribers DROP NOT NULL,
+    ALTER COLUMN pinned_activities DROP NOT NULL,
+    ALTER COLUMN emoji DROP NOT NULL,
+    ALTER COLUMN fields DROP NOT NULL,
+    ALTER COLUMN raw_fields DROP NOT NULL")
+  end
+end
index 1cfcb7ec7025e678885ea5b9c53b98156974dc02..c8e69cab5614b44fe0a3d3a7d1ee33af8e4e6a2c 100644 (file)
@@ -19,6 +19,7 @@
             "value": "schema:value",
             "sensitive": "as:sensitive",
             "litepub": "http://litepub.social/ns#",
+            "invisible": "litepub:invisible",
             "directMessage": "litepub:directMessage",
             "listMessage": {
                 "@id": "litepub:listMessage",
index 90f87a9909ed3960976019088645119c8958a917..87c486514008b84086e308efee4f7774cc85afad 100755 (executable)
@@ -140,12 +140,16 @@ else
        FULL_ARGS="$*"
 
        ACTION="$1"
-       shift
-
-       if [ "$(echo \"$1\" | grep \"^-\" >/dev/null)" = false ]; then
-               SUBACTION="$1"
+       if [ $# -gt 0 ]; then
                shift
        fi
+       echo "$1" | grep "^-" >/dev/null
+       if [ $? -eq 1 ]; then
+               SUBACTION="$1"
+               if [ $# -gt 0 ]; then
+                       shift
+               fi
+       fi
 
        if [ "$ACTION" = "update" ]; then
                update "$@"
index f430bdf75f5def543af0eab7a9cf9cceb4ae5cbf..863270022e8c0f8ac8675277114642cc46498598 100644 (file)
@@ -23,6 +23,39 @@ defmodule Pleroma.Conversation.ParticipationTest do
     assert %Pleroma.Conversation{} = participation.conversation
   end
 
+  test "for a new conversation or a reply, it doesn't mark the author's participation as unread" do
+    user = insert(:user)
+    other_user = insert(:user)
+
+    {:ok, _} =
+      CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"})
+
+    user = User.get_cached_by_id(user.id)
+    other_user = User.get_cached_by_id(other_user.id)
+
+    [%{read: true}] = Participation.for_user(user)
+    [%{read: false} = participation] = Participation.for_user(other_user)
+
+    assert User.get_cached_by_id(user.id).unread_conversation_count == 0
+    assert User.get_cached_by_id(other_user.id).unread_conversation_count == 1
+
+    {:ok, _} =
+      CommonAPI.post(other_user, %{
+        "status" => "Hey @#{user.nickname}.",
+        "visibility" => "direct",
+        "in_reply_to_conversation_id" => participation.id
+      })
+
+    user = User.get_cached_by_id(user.id)
+    other_user = User.get_cached_by_id(other_user.id)
+
+    [%{read: false}] = Participation.for_user(user)
+    [%{read: true}] = Participation.for_user(other_user)
+
+    assert User.get_cached_by_id(user.id).unread_conversation_count == 1
+    assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0
+  end
+
   test "for a new conversation, it sets the recipents of the participation" do
     user = insert(:user)
     other_user = insert(:user)
@@ -32,7 +65,7 @@ defmodule Pleroma.Conversation.ParticipationTest do
       CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"})
 
     user = User.get_cached_by_id(user.id)
-    other_user = User.get_cached_by_id(user.id)
+    other_user = User.get_cached_by_id(other_user.id)
     [participation] = Participation.for_user(user)
     participation = Pleroma.Repo.preload(participation, :recipients)
 
@@ -100,6 +133,20 @@ defmodule Pleroma.Conversation.ParticipationTest do
     refute participation.read
   end
 
+  test "it marks all the user's participations as read" do
+    user = insert(:user)
+    other_user = insert(:user)
+    participation1 = insert(:participation, %{read: false, user: user})
+    participation2 = insert(:participation, %{read: false, user: user})
+    participation3 = insert(:participation, %{read: false, user: other_user})
+
+    {:ok, _, [%{read: true}, %{read: true}]} = Participation.mark_all_as_read(user)
+
+    assert Participation.get(participation1.id).read == true
+    assert Participation.get(participation2.id).read == true
+    assert Participation.get(participation3.id).read == false
+  end
+
   test "gets all the participations for a user, ordered by updated at descending" do
     user = insert(:user)
     {:ok, activity_one} = CommonAPI.post(user, %{"status" => "x", "visibility" => "direct"})
@@ -169,4 +216,134 @@ defmodule Pleroma.Conversation.ParticipationTest do
     assert user in participation.recipients
     assert other_user in participation.recipients
   end
+
+  describe "blocking" do
+    test "when the user blocks a recipient, the existing conversations with them are marked as read" do
+      blocker = insert(:user)
+      blocked = insert(:user)
+      third_user = insert(:user)
+
+      {:ok, _direct1} =
+        CommonAPI.post(third_user, %{
+          "status" => "Hi @#{blocker.nickname}",
+          "visibility" => "direct"
+        })
+
+      {:ok, _direct2} =
+        CommonAPI.post(third_user, %{
+          "status" => "Hi @#{blocker.nickname}, @#{blocked.nickname}",
+          "visibility" => "direct"
+        })
+
+      {:ok, _direct3} =
+        CommonAPI.post(blocked, %{
+          "status" => "Hi @#{blocker.nickname}",
+          "visibility" => "direct"
+        })
+
+      {:ok, _direct4} =
+        CommonAPI.post(blocked, %{
+          "status" => "Hi @#{blocker.nickname}, @#{third_user.nickname}",
+          "visibility" => "direct"
+        })
+
+      assert [%{read: false}, %{read: false}, %{read: false}, %{read: false}] =
+               Participation.for_user(blocker)
+
+      assert User.get_cached_by_id(blocker.id).unread_conversation_count == 4
+
+      {:ok, blocker} = User.block(blocker, blocked)
+
+      # The conversations with the blocked user are marked as read
+      assert [%{read: true}, %{read: true}, %{read: true}, %{read: false}] =
+               Participation.for_user(blocker)
+
+      assert User.get_cached_by_id(blocker.id).unread_conversation_count == 1
+
+      # The conversation is not marked as read for the blocked user
+      assert [_, _, %{read: false}] = Participation.for_user(blocked)
+      assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1
+
+      # The conversation is not marked as read for the third user
+      assert [%{read: false}, _, _] = Participation.for_user(third_user)
+      assert User.get_cached_by_id(third_user.id).unread_conversation_count == 1
+    end
+
+    test "the new conversation with the blocked user is not marked as unread " do
+      blocker = insert(:user)
+      blocked = insert(:user)
+      third_user = insert(:user)
+
+      {:ok, blocker} = User.block(blocker, blocked)
+
+      # When the blocked user is the author
+      {:ok, _direct1} =
+        CommonAPI.post(blocked, %{
+          "status" => "Hi @#{blocker.nickname}",
+          "visibility" => "direct"
+        })
+
+      assert [%{read: true}] = Participation.for_user(blocker)
+      assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
+
+      # When the blocked user is a recipient
+      {:ok, _direct2} =
+        CommonAPI.post(third_user, %{
+          "status" => "Hi @#{blocker.nickname}, @#{blocked.nickname}",
+          "visibility" => "direct"
+        })
+
+      assert [%{read: true}, %{read: true}] = Participation.for_user(blocker)
+      assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
+
+      assert [%{read: false}, _] = Participation.for_user(blocked)
+      assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1
+    end
+
+    test "the conversation with the blocked user is not marked as unread on a reply" do
+      blocker = insert(:user)
+      blocked = insert(:user)
+      third_user = insert(:user)
+
+      {:ok, _direct1} =
+        CommonAPI.post(blocker, %{
+          "status" => "Hi @#{third_user.nickname}, @#{blocked.nickname}",
+          "visibility" => "direct"
+        })
+
+      {:ok, blocker} = User.block(blocker, blocked)
+      assert [%{read: true}] = Participation.for_user(blocker)
+      assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
+
+      assert [blocked_participation] = Participation.for_user(blocked)
+
+      # When it's a reply from the blocked user
+      {:ok, _direct2} =
+        CommonAPI.post(blocked, %{
+          "status" => "reply",
+          "visibility" => "direct",
+          "in_reply_to_conversation_id" => blocked_participation.id
+        })
+
+      assert [%{read: true}] = Participation.for_user(blocker)
+      assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
+
+      assert [third_user_participation] = Participation.for_user(third_user)
+
+      # When it's a reply from the third user
+      {:ok, _direct3} =
+        CommonAPI.post(third_user, %{
+          "status" => "reply",
+          "visibility" => "direct",
+          "in_reply_to_conversation_id" => third_user_participation.id
+        })
+
+      assert [%{read: true}] = Participation.for_user(blocker)
+      assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
+
+      # Marked as unread for the blocked user
+      assert [%{read: false}] = Participation.for_user(blocked)
+      assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1
+    end
+  end
 end
index 3168f3b9a76c963296c7a0843591efb6c31a0258..faf592d5f896df56c4a0b2ff42c48d9056b18197 100644 (file)
@@ -20,7 +20,7 @@ defmodule Pleroma.DigestEmailDaemonTest do
       |> Timex.to_naive_datetime()
 
     user2 = insert(:user, last_digest_emailed_at: date)
-    User.switch_email_notifications(user2, "digest", true)
+    {:ok, _} = User.switch_email_notifications(user2, "digest", true)
     CommonAPI.post(user, %{"status" => "hey @#{user2.nickname}!"})
 
     DigestEmailDaemon.perform()
index 963565f7c27ea08c71cee4641d02de7dd067c24c..9e145977ef847805ec3cf95890bf7bf795143bb6 100644 (file)
@@ -36,7 +36,7 @@ defmodule Pleroma.Emails.UserEmailTest do
 
   test "build account confirmation email" do
     config = Pleroma.Config.get(:instance)
-    user = insert(:user, info: %Pleroma.User.Info{confirmation_token: "conf-token"})
+    user = insert(:user, confirmation_token: "conf-token")
     email = UserEmail.account_confirmation_email(user)
     assert email.from == {config[:name], config[:notify_email]}
     assert email.to == [{user.name, user.email}]
diff --git a/test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.json b/test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.json
new file mode 100644 (file)
index 0000000..4b7b4df
--- /dev/null
@@ -0,0 +1 @@
+{"@context":["https://www.w3.org/ns/activitystreams","https://shitposter.club/schemas/litepub-0.1.jsonld",{"@language":"und"}],"actor":"https://shitposter.club/users/moonman","attachment":[],"attributedTo":"https://shitposter.club/users/moonman","cc":["https://shitposter.club/users/moonman/followers"],"content":"@<a href=\"https://shitposter.club/users/9655\" class=\"h-card mention\" title=\"Solidarity for Pigs\">neimzr4luzerz</a> @<a href=\"https://gs.smuglo.li/user/2326\" class=\"h-card mention\" title=\"Dolus_McHonest\">dolus</a> childhood poring over Strong's concordance and a koine Greek dictionary, fast forward to 2017 and some fuckstick who translates japanese jackoff material tells me you just need to make it sound right in English","context":"tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26","conversation":"tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26","id":"tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment","inReplyTo":"tag:shitposter.club,2017-05-05:noticeId=2827849:objectType=comment","inReplyToStatusId":2827849,"published":"2017-05-05T08:51:48Z","sensitive":false,"summary":null,"tag":[],"to":["https://www.w3.org/ns/activitystreams#Public"],"type":"Note"}
\ No newline at end of file
diff --git a/test/fixtures/tesla_mock/moonman@shitposter.club.json b/test/fixtures/tesla_mock/moonman@shitposter.club.json
new file mode 100644 (file)
index 0000000..8f9ced1
--- /dev/null
@@ -0,0 +1 @@
+{"@context":["https://www.w3.org/ns/activitystreams","https://shitposter.club/schemas/litepub-0.1.jsonld",{"@language":"und"}],"attachment":[],"endpoints":{"oauthAuthorizationEndpoint":"https://shitposter.club/oauth/authorize","oauthRegistrationEndpoint":"https://shitposter.club/api/v1/apps","oauthTokenEndpoint":"https://shitposter.club/oauth/token","sharedInbox":"https://shitposter.club/inbox"},"followers":"https://shitposter.club/users/moonman/followers","following":"https://shitposter.club/users/moonman/following","icon":{"type":"Image","url":"https://shitposter.club/media/bda6e00074f6a02cbf32ddb0abec08151eb4c795e580927ff7ad638d00cde4c8.jpg?name=blob.jpg"},"id":"https://shitposter.club/users/moonman","image":{"type":"Image","url":"https://shitposter.club/media/4eefb90d-cdb2-2b4f-5f29-7612856a99d2/4eefb90d-cdb2-2b4f-5f29-7612856a99d2.jpeg"},"inbox":"https://shitposter.club/users/moonman/inbox","manuallyApprovesFollowers":false,"name":"Captain Howdy","outbox":"https://shitposter.club/users/moonman/outbox","preferredUsername":"moonman","publicKey":{"id":"https://shitposter.club/users/moonman#main-key","owner":"https://shitposter.club/users/moonman","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnOTitJ19ZqcOZHwSXQUM\nJq9ip4GNblp83LgwG1t5c2h2iaI3fXMsB4EaEBs8XHsoSFyDeDNRSPE3mtVgOnWv\n1eaXWMDerBT06th6DrElD9k5IoEPtZRY4HtZa1xGnte7+6RjuPOzZ1fR9C8WxGgi\nwb9iOUMhazpo85fC3iKCAL5XhiuA3Nas57MDJgueeI9BF+2oFelFZdMSWwG96uch\niDfp8nfpkmzYI6SWbylObjm8RsfZbGTosLHwWyJPEITeYI/5M0XwJe9dgVI1rVNU\n52kplWOGTo1rm6V0AMHaYAd9RpiXxe8xt5OeranrsE/5LvEQUl0fz7SE36YmsOaH\nTwIDAQAB\n-----END PUBLIC KEY-----\n\n"},"summary":"EMAIL:shitposterclub@gmail.com<br>XMPP: moon@talk.shitposter.club<br>PRONOUNS: none of your business<br><br>Purported leftist kike piece of shit","tag":[],"type":"Person","url":"https://shitposter.club/users/moonman"}
\ No newline at end of file
diff --git a/test/fixtures/tesla_mock/relay@mastdon.example.org.json b/test/fixtures/tesla_mock/relay@mastdon.example.org.json
new file mode 100644 (file)
index 0000000..c1fab7d
--- /dev/null
@@ -0,0 +1,55 @@
+{
+  "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {
+    "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+    "sensitive": "as:sensitive",
+    "movedTo": "as:movedTo",
+    "Hashtag": "as:Hashtag",
+    "ostatus": "http://ostatus.org#",
+    "atomUri": "ostatus:atomUri",
+    "inReplyToAtomUri": "ostatus:inReplyToAtomUri",
+    "conversation": "ostatus:conversation",
+    "toot": "http://joinmastodon.org/ns#",
+    "Emoji": "toot:Emoji"
+  }],
+  "id": "http://mastodon.example.org/users/admin",
+  "type": "Application",
+  "invisible": true,
+  "following": "http://mastodon.example.org/users/admin/following",
+  "followers": "http://mastodon.example.org/users/admin/followers",
+  "inbox": "http://mastodon.example.org/users/admin/inbox",
+  "outbox": "http://mastodon.example.org/users/admin/outbox",
+  "preferredUsername": "admin",
+  "name": null,
+  "summary": "\u003cp\u003e\u003c/p\u003e",
+  "url": "http://mastodon.example.org/@admin",
+  "manuallyApprovesFollowers": false,
+  "publicKey": {
+    "id": "http://mastodon.example.org/users/admin#main-key",
+    "owner": "http://mastodon.example.org/users/admin",
+    "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtc4Tir+3ADhSNF6VKrtW\nOU32T01w7V0yshmQei38YyiVwVvFu8XOP6ACchkdxbJ+C9mZud8qWaRJKVbFTMUG\nNX4+6Q+FobyuKrwN7CEwhDALZtaN2IPbaPd6uG1B7QhWorrY+yFa8f2TBM3BxnUy\nI4T+bMIZIEYG7KtljCBoQXuTQmGtuffO0UwJksidg2ffCF5Q+K//JfQagJ3UzrR+\nZXbKMJdAw4bCVJYs4Z5EhHYBwQWiXCyMGTd7BGlmMkY6Av7ZqHKC/owp3/0EWDNz\nNqF09Wcpr3y3e8nA10X40MJqp/wR+1xtxp+YGbq/Cj5hZGBG7etFOmIpVBrDOhry\nBwIDAQAB\n-----END PUBLIC KEY-----\n"
+  },
+  "attachment": [{
+      "type": "PropertyValue",
+      "name": "foo",
+      "value": "bar"
+    },
+    {
+      "type": "PropertyValue",
+      "name": "foo1",
+      "value": "bar1"
+    }
+  ],
+  "endpoints": {
+    "sharedInbox": "http://mastodon.example.org/inbox"
+  },
+  "icon": {
+    "type": "Image",
+    "mediaType": "image/jpeg",
+    "url": "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
+  },
+  "image": {
+    "type": "Image",
+    "mediaType": "image/png",
+    "url": "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
+  }
+}
index 3bff51527592882801175372cbcab1f91b68d137..087bdbcc2b8a77d08c809595c1040e50fdb81c0f 100644 (file)
@@ -125,10 +125,10 @@ defmodule Pleroma.FormatterTest do
       gsimg = insert(:user, %{nickname: "gsimg"})
 
       archaeme =
-        insert(:user, %{
+        insert(:user,
           nickname: "archa_eme_",
-          info: %User.Info{source_data: %{"url" => "https://archeme/@archa_eme_"}}
-        })
+          source_data: %{"url" => "https://archeme/@archa_eme_"}
+        )
 
       archaeme_remote = insert(:user, %{nickname: "archaeme@archae.me"})
 
diff --git a/test/marker_test.exs b/test/marker_test.exs
new file mode 100644 (file)
index 0000000..04bd67f
--- /dev/null
@@ -0,0 +1,51 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.MarkerTest do
+  use Pleroma.DataCase
+  alias Pleroma.Marker
+
+  import Pleroma.Factory
+
+  describe "get_markers/2" do
+    test "returns user markers" do
+      user = insert(:user)
+      marker = insert(:marker, user: user)
+      insert(:marker, timeline: "home", user: user)
+      assert Marker.get_markers(user, ["notifications"]) == [refresh_record(marker)]
+    end
+  end
+
+  describe "upsert/2" do
+    test "creates a marker" do
+      user = insert(:user)
+
+      {:ok, %{"notifications" => %Marker{} = marker}} =
+        Marker.upsert(
+          user,
+          %{"notifications" => %{"last_read_id" => "34"}}
+        )
+
+      assert marker.timeline == "notifications"
+      assert marker.last_read_id == "34"
+      assert marker.lock_version == 0
+    end
+
+    test "updates exist marker" do
+      user = insert(:user)
+      marker = insert(:marker, user: user, last_read_id: "8909")
+
+      {:ok, %{"notifications" => %Marker{}}} =
+        Marker.upsert(
+          user,
+          %{"notifications" => %{"last_read_id" => "9909"}}
+        )
+
+      marker = refresh_record(marker)
+      assert marker.timeline == "notifications"
+      assert marker.last_read_id == "9909"
+      assert marker.lock_version == 0
+    end
+  end
+end
index a39a00e0221ff2b3e8265540da28ab53980ec4a1..4240f6a654b55b8eef529269b1757cb24867c202 100644 (file)
@@ -12,8 +12,8 @@ defmodule Pleroma.ModerationLogTest do
 
   describe "user moderation" do
     setup do
-      admin = insert(:user, info: %{is_admin: true})
-      moderator = insert(:user, info: %{is_moderator: true})
+      admin = insert(:user, is_admin: true)
+      moderator = insert(:user, is_moderator: true)
       subject1 = insert(:user)
       subject2 = insert(:user)
 
@@ -24,13 +24,13 @@ defmodule Pleroma.ModerationLogTest do
       {:ok, _} =
         ModerationLog.insert_log(%{
           actor: moderator,
-          subject: subject1,
+          subject: [subject1],
           action: "delete"
         })
 
       log = Repo.one(ModerationLog)
 
-      assert log.data["message"] == "@#{moderator.nickname} deleted user @#{subject1.nickname}"
+      assert log.data["message"] == "@#{moderator.nickname} deleted users: @#{subject1.nickname}"
     end
 
     test "logging user creation by moderator", %{
@@ -128,7 +128,7 @@ defmodule Pleroma.ModerationLogTest do
       {:ok, _} =
         ModerationLog.insert_log(%{
           actor: moderator,
-          subject: subject1,
+          subject: [subject1],
           action: "grant",
           permission: "moderator"
         })
@@ -142,7 +142,7 @@ defmodule Pleroma.ModerationLogTest do
       {:ok, _} =
         ModerationLog.insert_log(%{
           actor: moderator,
-          subject: subject1,
+          subject: [subject1],
           action: "revoke",
           permission: "moderator"
         })
index 54c0f987753158176467f9a3edca55fba47f4a94..f8d42922322353a2d509dc8fc2c4c33313db186a 100644 (file)
@@ -136,7 +136,7 @@ defmodule Pleroma.NotificationTest do
 
     test "it disables notifications from followers" do
       follower = insert(:user)
-      followed = insert(:user, info: %{notification_settings: %{"followers" => false}})
+      followed = insert(:user, notification_settings: %{"followers" => false})
       User.follow(follower, followed)
       {:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"})
       refute Notification.create_notification(activity, followed)
@@ -144,13 +144,13 @@ defmodule Pleroma.NotificationTest do
 
     test "it disables notifications from non-followers" do
       follower = insert(:user)
-      followed = insert(:user, info: %{notification_settings: %{"non_followers" => false}})
+      followed = insert(:user, notification_settings: %{"non_followers" => false})
       {:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"})
       refute Notification.create_notification(activity, followed)
     end
 
     test "it disables notifications from people the user follows" do
-      follower = insert(:user, info: %{notification_settings: %{"follows" => false}})
+      follower = insert(:user, notification_settings: %{"follows" => false})
       followed = insert(:user)
       User.follow(follower, followed)
       follower = Repo.get(User, follower.id)
@@ -159,7 +159,7 @@ defmodule Pleroma.NotificationTest do
     end
 
     test "it disables notifications from people the user does not follow" do
-      follower = insert(:user, info: %{notification_settings: %{"non_follows" => false}})
+      follower = insert(:user, notification_settings: %{"non_follows" => false})
       followed = insert(:user)
       {:ok, activity} = CommonAPI.post(followed, %{"status" => "hey @#{follower.nickname}"})
       refute Notification.create_notification(activity, follower)
@@ -683,7 +683,7 @@ defmodule Pleroma.NotificationTest do
       assert Notification.for_user(user) == []
     end
 
-    test "it returns notifications for muted user with notifications and with_muted parameter" do
+    test "it returns notifications from a muted user when with_muted is set" do
       user = insert(:user)
       muted = insert(:user)
       {:ok, user} = User.mute(user, muted)
@@ -693,27 +693,27 @@ defmodule Pleroma.NotificationTest do
       assert length(Notification.for_user(user, %{with_muted: true})) == 1
     end
 
-    test "it returns notifications for blocked user and with_muted parameter" do
+    test "it doesn't return notifications from a blocked user when with_muted is set" do
       user = insert(:user)
       blocked = insert(:user)
       {:ok, user} = User.block(user, blocked)
 
       {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
 
-      assert length(Notification.for_user(user, %{with_muted: true})) == 1
+      assert length(Notification.for_user(user, %{with_muted: true})) == 0
     end
 
-    test "it returns notificatitons for blocked domain and with_muted parameter" do
+    test "it doesn't return notifications from a domain-blocked user when with_muted is set" do
       user = insert(:user)
       blocked = insert(:user, ap_id: "http://some-domain.com")
       {:ok, user} = User.block_domain(user, "some-domain.com")
 
       {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
 
-      assert length(Notification.for_user(user, %{with_muted: true})) == 1
+      assert length(Notification.for_user(user, %{with_muted: true})) == 0
     end
 
-    test "it returns notifications for muted thread with_muted parameter" do
+    test "it returns notifications from muted threads when with_muted is set" do
       user = insert(:user)
       another_user = insert(:user)
 
index 61cd1b41228d896e72f4596789e331ce3e2a2f66..0dc2728b9a0a44a70d7455171120d5f5a6a11ff2 100644 (file)
@@ -65,7 +65,7 @@ defmodule Pleroma.Object.ContainmentTest do
       assert capture_log(fn ->
                {:error, _} = User.get_or_fetch_by_ap_id("https://n1u.moe/users/rye")
              end) =~
-               "[error] Could not decode user at fetch https://n1u.moe/users/rye, {:error, :error}"
+               "[error] Could not decode user at fetch https://n1u.moe/users/rye"
     end
   end
 
index 895a73d2c893b7ad201fbe6bef1c26dd085ab6ee..9ae6b015d6f74ef3e4efa57a8ccd18034408dbed 100644 (file)
@@ -27,31 +27,16 @@ defmodule Pleroma.Object.FetcherTest do
   end
 
   describe "actor origin containment" do
-    test_with_mock "it rejects objects with a bogus origin",
-                   Pleroma.Web.OStatus,
-                   [:passthrough],
-                   [] do
+    test "it rejects objects with a bogus origin" do
       {:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity.json")
-
-      refute called(Pleroma.Web.OStatus.fetch_activity_from_url(:_))
     end
 
-    test_with_mock "it rejects objects when attributedTo is wrong (variant 1)",
-                   Pleroma.Web.OStatus,
-                   [:passthrough],
-                   [] do
+    test "it rejects objects when attributedTo is wrong (variant 1)" do
       {:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity2.json")
-
-      refute called(Pleroma.Web.OStatus.fetch_activity_from_url(:_))
     end
 
-    test_with_mock "it rejects objects when attributedTo is wrong (variant 2)",
-                   Pleroma.Web.OStatus,
-                   [:passthrough],
-                   [] do
+    test "it rejects objects when attributedTo is wrong (variant 2)" do
       {:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity3.json")
-
-      refute called(Pleroma.Web.OStatus.fetch_activity_from_url(:_))
     end
   end
 
@@ -71,24 +56,6 @@ defmodule Pleroma.Object.FetcherTest do
 
       assert object == object_again
     end
-
-    test "it works with objects only available via Ostatus" do
-      {:ok, object} = Fetcher.fetch_object_from_id("https://shitposter.club/notice/2827873")
-      assert activity = Activity.get_create_by_object_ap_id(object.data["id"])
-      assert activity.data["id"]
-
-      {:ok, object_again} = Fetcher.fetch_object_from_id("https://shitposter.club/notice/2827873")
-
-      assert object == object_again
-    end
-
-    test "it correctly stitches up conversations between ostatus and ap" do
-      last = "https://mstdn.io/users/mayuutann/statuses/99568293732299394"
-      {:ok, object} = Fetcher.fetch_object_from_id(last)
-
-      object = Object.get_by_ap_id(object.data["inReplyTo"])
-      assert object
-    end
   end
 
   describe "implementation quirks" do
index e1d4b391f76978dc54bbd29b8b5bf3634e61ff62..c94a62c102e9bd82d1fe53801110ac8810d0ee2f 100644 (file)
@@ -37,6 +37,6 @@ defmodule Pleroma.Plugs.AdminSecretAuthenticationPlugTest do
       %{conn | params: %{"admin_token" => "password123"}}
       |> AdminSecretAuthenticationPlug.call(%{})
 
-    assert conn.assigns[:user].info.is_admin
+    assert conn.assigns[:user].is_admin
   end
 end
index c0fafcab1fec842985234f55cdbdddb91c9ce982..996a7d77b11223078eefe67e564feaea4d4e399d 100644 (file)
@@ -17,7 +17,7 @@ defmodule Pleroma.Plugs.UserEnabledPlugTest do
   end
 
   test "with a user that is deactivated, it removes that user", %{conn: conn} do
-    user = insert(:user, info: %{deactivated: true})
+    user = insert(:user, deactivated: true)
 
     conn =
       conn
index 9e05fff18aca5a3277063342bffacf8201fccf27..136dcc54ed2ce143873b1fce4a3c2bb885ad1fed 100644 (file)
@@ -9,7 +9,7 @@ defmodule Pleroma.Plugs.UserIsAdminPlugTest do
   import Pleroma.Factory
 
   test "accepts a user that is admin" do
-    user = insert(:user, info: %{is_admin: true})
+    user = insert(:user, is_admin: true)
 
     conn =
       build_conn()
diff --git a/test/safe_jsonb_set_test.exs b/test/safe_jsonb_set_test.exs
new file mode 100644 (file)
index 0000000..7485405
--- /dev/null
@@ -0,0 +1,12 @@
+defmodule Pleroma.SafeJsonbSetTest do
+  use Pleroma.DataCase
+
+  test "it doesn't wipe the object when asked to set the value to NULL" do
+    assert %{rows: [[%{"key" => "value", "test" => nil}]]} =
+             Ecto.Adapters.SQL.query!(
+               Pleroma.Repo,
+               "select safe_jsonb_set('{\"key\": \"value\"}'::jsonb, '{test}', NULL);",
+               []
+             )
+  end
+end
index 96c8ba07ac213ab88dcef1a8611bbbeeacfc6c5b..15cf10fb6852daee731471b8f82f5eb0fbcf54d6 100644 (file)
@@ -42,7 +42,7 @@ defmodule Pleroma.SignatureTest do
     test "it returns key" do
       expected_result = {:ok, @rsa_public_key}
 
-      user = insert(:user, %{info: %{source_data: %{"publicKey" => @public_key}}})
+      user = insert(:user, source_data: %{"publicKey" => @public_key})
 
       assert Signature.fetch_public_key(make_fake_conn(user.ap_id)) == expected_result
     end
@@ -54,7 +54,7 @@ defmodule Pleroma.SignatureTest do
     end
 
     test "it returns error if public key is empty" do
-      user = insert(:user, %{info: %{source_data: %{"publicKey" => %{}}}})
+      user = insert(:user, source_data: %{"publicKey" => %{}})
 
       assert Signature.fetch_public_key(make_fake_conn(user.ap_id)) == {:error, :error}
     end
@@ -69,8 +69,7 @@ defmodule Pleroma.SignatureTest do
 
     test "it returns error when not found user" do
       assert capture_log(fn ->
-               assert Signature.refetch_public_key(make_fake_conn("test-ap_id")) ==
-                        {:error, {:error, :ok}}
+               {:error, _} = Signature.refetch_public_key(make_fake_conn("test-ap_id"))
              end) =~ "[error] Could not decode user"
     end
   end
index b180844cd90acc7fde68ee3d95543bc89c2a1529..41e2b8004d515905d1d0833052c862d1a5af0bab 100644 (file)
@@ -281,26 +281,6 @@ defmodule Pleroma.Factory do
     }
   end
 
-  def websub_subscription_factory do
-    %Pleroma.Web.Websub.WebsubServerSubscription{
-      topic: "http://example.org",
-      callback: "http://example.org/callback",
-      secret: "here's a secret",
-      valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 100),
-      state: "requested"
-    }
-  end
-
-  def websub_client_subscription_factory do
-    %Pleroma.Web.Websub.WebsubClientSubscription{
-      topic: "http://example.org",
-      secret: "here's a secret",
-      valid_until: nil,
-      state: "requested",
-      subscribers: []
-    }
-  end
-
   def oauth_app_factory do
     %Pleroma.Web.OAuth.App{
       client_name: "Some client",
@@ -397,4 +377,13 @@ defmodule Pleroma.Factory do
         )
     }
   end
+
+  def marker_factory do
+    %Pleroma.Marker{
+      user: build(:user),
+      timeline: "notifications",
+      lock_version: 0,
+      last_read_id: "1"
+    }
+  end
 end
index 4feb57f3ad84154251501f1538613bd8eaaaa0a2..eba22c40b1dabe5c3d09d46b00ed67a8b68423fb 100644 (file)
@@ -38,6 +38,14 @@ defmodule HttpRequestMock do
      }}
   end
 
+  def get("https://shitposter.club/users/moonman", _, _, _) do
+    {:ok,
+     %Tesla.Env{
+       status: 200,
+       body: File.read!("test/fixtures/tesla_mock/moonman@shitposter.club.json")
+     }}
+  end
+
   def get("https://mastodon.social/users/emelie/statuses/101849165031453009", _, _, _) do
     {:ok,
      %Tesla.Env{
@@ -340,6 +348,14 @@ defmodule HttpRequestMock do
      }}
   end
 
+  def get("http://mastodon.example.org/users/relay", _, _, Accept: "application/activity+json") do
+    {:ok,
+     %Tesla.Env{
+       status: 200,
+       body: File.read!("test/fixtures/tesla_mock/relay@mastdon.example.org.json")
+     }}
+  end
+
   def get("http://mastodon.example.org/users/gargron", _, _, Accept: "application/activity+json") do
     {:error, :nxdomain}
   end
@@ -620,7 +636,7 @@ defmodule HttpRequestMock do
     {:ok,
      %Tesla.Env{
        status: 200,
-       body: File.read!("test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.html")
+       body: File.read!("test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.json")
      }}
   end
 
index 6035da3c31b41988800b1567220f5491b52f20c0..bb5dc88f8c3b19223fe714b38becaa6d50b929a7 100644 (file)
@@ -22,18 +22,18 @@ defmodule Mix.Tasks.Pleroma.CountStatusesTest do
     user = refresh_record(user)
     user2 = refresh_record(user2)
 
-    assert %{info: %{note_count: 2}} = user
-    assert %{info: %{note_count: 1}} = user2
+    assert %{note_count: 2} = user
+    assert %{note_count: 1} = user2
 
-    {:ok, user} = User.update_info(user, &User.Info.set_note_count(&1, 0))
-    {:ok, user2} = User.update_info(user2, &User.Info.set_note_count(&1, 0))
+    {:ok, user} = User.update_note_count(user, 0)
+    {:ok, user2} = User.update_note_count(user2, 0)
 
-    assert %{info: %{note_count: 0}} = user
-    assert %{info: %{note_count: 0}} = user2
+    assert %{note_count: 0} = user
+    assert %{note_count: 0} = user2
 
     assert capture_io(fn -> Mix.Tasks.Pleroma.CountStatuses.run([]) end) == "Done\n"
 
-    assert %{info: %{note_count: 2}} = refresh_record(user)
-    assert %{info: %{note_count: 1}} = refresh_record(user2)
+    assert %{note_count: 2} = refresh_record(user)
+    assert %{note_count: 1} = refresh_record(user2)
   end
 end
index b63dcac002e5cfce7c045e5cd64231f5dce3f594..b66324e5e75975eeb1e83fbf763fb7d2271db20e 100644 (file)
@@ -72,26 +72,25 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest do
   describe "running update_users_following_followers_counts" do
     test "following and followers count are updated" do
       [user, user2] = insert_pair(:user)
-      {:ok, %User{following: following, info: info} = user} = User.follow(user, user2)
+      {:ok, %User{following: following} = user} = User.follow(user, user2)
 
       assert length(following) == 2
-      assert info.follower_count == 0
+      assert user.follower_count == 0
 
       {:ok, user} =
         user
-        |> Ecto.Changeset.change(%{following: following ++ following})
-        |> User.change_info(&Ecto.Changeset.change(&1, %{follower_count: 3}))
+        |> Ecto.Changeset.change(%{following: following ++ following, follower_count: 3})
         |> Repo.update()
 
       assert length(user.following) == 4
-      assert user.info.follower_count == 3
+      assert user.follower_count == 3
 
       assert :ok == Mix.Tasks.Pleroma.Database.run(["update_users_following_followers_counts"])
 
       user = User.get_by_id(user.id)
 
       assert length(user.following) == 2
-      assert user.info.follower_count == 0
+      assert user.follower_count == 0
     end
   end
 
index cf12d9ed6ef7c43cf570d5b7e58358f0ce1b19aa..f024f92ae8c5ed2fdaa541bf5b845d25e9382985 100644 (file)
@@ -58,8 +58,8 @@ defmodule Mix.Tasks.Pleroma.UserTest do
       assert user.name == unsaved.name
       assert user.email == unsaved.email
       assert user.bio == unsaved.bio
-      assert user.info.is_moderator
-      assert user.info.is_admin
+      assert user.is_moderator
+      assert user.is_admin
     end
 
     test "user is not created" do
@@ -113,11 +113,11 @@ defmodule Mix.Tasks.Pleroma.UserTest do
       assert message =~ " deactivated"
 
       user = User.get_cached_by_nickname(user.nickname)
-      assert user.info.deactivated
+      assert user.deactivated
     end
 
     test "user is activated" do
-      user = insert(:user, info: %{deactivated: true})
+      user = insert(:user, deactivated: true)
 
       Mix.Tasks.Pleroma.User.run(["toggle_activated", user.nickname])
 
@@ -125,7 +125,7 @@ defmodule Mix.Tasks.Pleroma.UserTest do
       assert message =~ " activated"
 
       user = User.get_cached_by_nickname(user.nickname)
-      refute user.info.deactivated
+      refute user.deactivated
     end
 
     test "no user to toggle" do
@@ -155,7 +155,7 @@ defmodule Mix.Tasks.Pleroma.UserTest do
 
       user = User.get_cached_by_nickname(user.nickname)
       assert Enum.empty?(user.following)
-      assert user.info.deactivated
+      assert user.deactivated
     end
 
     test "no user to unsubscribe" do
@@ -182,13 +182,13 @@ defmodule Mix.Tasks.Pleroma.UserTest do
       assert message =~ ~r/Admin status .* true/
 
       user = User.get_cached_by_nickname(user.nickname)
-      assert user.info.is_moderator
-      assert user.info.locked
-      assert user.info.is_admin
+      assert user.is_moderator
+      assert user.locked
+      assert user.is_admin
     end
 
     test "All statuses unset" do
-      user = insert(:user, info: %{is_moderator: true, locked: true, is_admin: true})
+      user = insert(:user, locked: true, is_moderator: true, is_admin: true)
 
       Mix.Tasks.Pleroma.User.run([
         "set",
@@ -208,9 +208,9 @@ defmodule Mix.Tasks.Pleroma.UserTest do
       assert message =~ ~r/Admin status .* false/
 
       user = User.get_cached_by_nickname(user.nickname)
-      refute user.info.is_moderator
-      refute user.info.locked
-      refute user.info.is_admin
+      refute user.is_moderator
+      refute user.locked
+      refute user.is_admin
     end
 
     test "no user to set status" do
@@ -358,28 +358,28 @@ defmodule Mix.Tasks.Pleroma.UserTest do
 
   describe "running toggle_confirmed" do
     test "user is confirmed" do
-      %{id: id, nickname: nickname} = insert(:user, info: %{confirmation_pending: false})
+      %{id: id, nickname: nickname} = insert(:user, confirmation_pending: false)
 
       assert :ok = Mix.Tasks.Pleroma.User.run(["toggle_confirmed", nickname])
       assert_received {:mix_shell, :info, [message]}
       assert message == "#{nickname} needs confirmation."
 
       user = Repo.get(User, id)
-      assert user.info.confirmation_pending
-      assert user.info.confirmation_token
+      assert user.confirmation_pending
+      assert user.confirmation_token
     end
 
     test "user is not confirmed" do
       %{id: id, nickname: nickname} =
-        insert(:user, info: %{confirmation_pending: true, confirmation_token: "some token"})
+        insert(:user, confirmation_pending: true, confirmation_token: "some token")
 
       assert :ok = Mix.Tasks.Pleroma.User.run(["toggle_confirmed", nickname])
       assert_received {:mix_shell, :info, [message]}
       assert message == "#{nickname} doesn't need confirmation."
 
       user = Repo.get(User, id)
-      refute user.info.confirmation_pending
-      refute user.info.confirmation_token
+      refute user.confirmation_pending
+      refute user.confirmation_token
     end
 
     test "it prints an error message when user is not exist" do
diff --git a/test/user_info_test.exs b/test/user_info_test.exs
deleted file mode 100644 (file)
index 2d79559..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-defmodule Pleroma.UserInfoTest do
-  alias Pleroma.Repo
-  alias Pleroma.User.Info
-
-  use Pleroma.DataCase
-
-  import Pleroma.Factory
-
-  describe "update_email_notifications/2" do
-    setup do
-      user = insert(:user, %{info: %{email_notifications: %{"digest" => true}}})
-
-      {:ok, user: user}
-    end
-
-    test "Notifications are updated", %{user: user} do
-      true = user.info.email_notifications["digest"]
-      changeset = Info.update_email_notifications(user.info, %{"digest" => false})
-      assert changeset.valid?
-      {:ok, result} = Ecto.Changeset.apply_action(changeset, :insert)
-      assert result.email_notifications["digest"] == false
-    end
-  end
-end
index f7ab312872c0c820a8919dd318513e227c47d6db..78a02d5364c5434fc2dda5f3c83f5caa96512daf 100644 (file)
@@ -65,21 +65,6 @@ defmodule Pleroma.UserSearchTest do
       assert [u2.id, u1.id] == Enum.map(User.search("bar word"), & &1.id)
     end
 
-    test "finds users, ranking by similarity" do
-      u1 = insert(:user, %{name: "lain"})
-      _u2 = insert(:user, %{name: "ean"})
-      u3 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social"})
-      u4 = insert(:user, %{nickname: "lain@pleroma.soykaf.com"})
-
-      assert [u4.id, u3.id, u1.id] == Enum.map(User.search("lain@ple", for_user: u1), & &1.id)
-    end
-
-    test "finds users, handling misspelled requests" do
-      u1 = insert(:user, %{name: "lain"})
-
-      assert [u1.id] == Enum.map(User.search("laiin"), & &1.id)
-    end
-
     test "finds users, boosting ranks of friends and followers" do
       u1 = insert(:user)
       u2 = insert(:user, %{name: "Doe"})
@@ -163,17 +148,6 @@ defmodule Pleroma.UserSearchTest do
       Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
     end
 
-    test "finds a user whose name is nil" do
-      _user = insert(:user, %{name: "notamatch", nickname: "testuser@pleroma.amplifie.red"})
-      user_two = insert(:user, %{name: nil, nickname: "lain@pleroma.soykaf.com"})
-
-      assert user_two ==
-               User.search("lain@pleroma.soykaf.com")
-               |> List.first()
-               |> Map.put(:search_rank, nil)
-               |> Map.put(:search_type, nil)
-    end
-
     test "does not yield false-positive matches" do
       insert(:user, %{name: "John Doe"})
 
index 019e7b400c32f4b2e5957fb303caa96f956c2eda..92de31c748a3c5259fd772b0a00a55cc63e293c5 100644 (file)
@@ -68,7 +68,7 @@ defmodule Pleroma.UserTest do
 
   test "returns all pending follow requests" do
     unlocked = insert(:user)
-    locked = insert(:user, %{info: %{locked: true}})
+    locked = insert(:user, locked: true)
     follower = insert(:user)
 
     CommonAPI.follow(follower, unlocked)
@@ -81,7 +81,7 @@ defmodule Pleroma.UserTest do
   end
 
   test "doesn't return already accepted or duplicate follow requests" do
-    locked = insert(:user, %{info: %{locked: true}})
+    locked = insert(:user, locked: true)
     pending_follower = insert(:user)
     accepted_follower = insert(:user)
 
@@ -95,7 +95,7 @@ defmodule Pleroma.UserTest do
   end
 
   test "clears follow requests when requester is blocked" do
-    followed = insert(:user, %{info: %{locked: true}})
+    followed = insert(:user, locked: true)
     follower = insert(:user)
 
     CommonAPI.follow(follower, followed)
@@ -151,14 +151,14 @@ defmodule Pleroma.UserTest do
     user = User.get_cached_by_id(user.id)
 
     followed = User.get_cached_by_ap_id(followed.ap_id)
-    assert followed.info.follower_count == 1
+    assert followed.follower_count == 1
 
     assert User.ap_followers(followed) in user.following
   end
 
   test "can't follow a deactivated users" do
     user = insert(:user)
-    followed = insert(:user, info: %{deactivated: true})
+    followed = insert(:user, %{deactivated: true})
 
     {:error, _} = User.follow(user, followed)
   end
@@ -182,31 +182,14 @@ defmodule Pleroma.UserTest do
   end
 
   test "local users do not automatically follow local locked accounts" do
-    follower = insert(:user, info: %{locked: true})
-    followed = insert(:user, info: %{locked: true})
+    follower = insert(:user, locked: true)
+    followed = insert(:user, locked: true)
 
     {:ok, follower} = User.maybe_direct_follow(follower, followed)
 
     refute User.following?(follower, followed)
   end
 
-  # This is a somewhat useless test.
-  # test "following a remote user will ensure a websub subscription is present" do
-  #   user = insert(:user)
-  #   {:ok, followed} = OStatus.make_user("shp@social.heldscal.la")
-
-  #   assert followed.local == false
-
-  #   {:ok, user} = User.follow(user, followed)
-  #   assert User.ap_followers(followed) in user.following
-
-  #   query = from w in WebsubClientSubscription,
-  #   where: w.topic == ^followed.info["topic"]
-  #   websub = Repo.one(query)
-
-  #   assert websub
-  # end
-
   describe "unfollow/2" do
     setup do
       setting = Pleroma.Config.get([:instance, :external_user_synchronization])
@@ -400,8 +383,8 @@ defmodule Pleroma.UserTest do
 
       {:ok, user} = Repo.insert(changeset)
 
-      assert user.info.confirmation_pending
-      assert user.info.confirmation_token
+      assert user.confirmation_pending
+      assert user.confirmation_token
     end
 
     test "it creates confirmed user if :confirmed option is given" do
@@ -410,8 +393,8 @@ defmodule Pleroma.UserTest do
 
       {:ok, user} = Repo.insert(changeset)
 
-      refute user.info.confirmation_pending
-      refute user.info.confirmation_token
+      refute user.confirmation_pending
+      refute user.confirmation_token
     end
   end
 
@@ -474,11 +457,6 @@ defmodule Pleroma.UserTest do
       assert user == fetched_user
     end
 
-    test "fetches an external user via ostatus if no user exists" do
-      {:ok, fetched_user} = User.get_or_fetch_by_nickname("shp@social.heldscal.la")
-      assert fetched_user.nickname == "shp@social.heldscal.la"
-    end
-
     test "returns nil if no user could be fetched" do
       {:error, fetched_user} = User.get_or_fetch_by_nickname("nonexistant@social.heldscal.la")
       assert fetched_user == "not found nonexistant@social.heldscal.la"
@@ -505,7 +483,8 @@ defmodule Pleroma.UserTest do
       assert orig_user.last_refreshed_at == a_week_ago
 
       {:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin")
-      assert user.info.source_data["endpoints"]
+
+      assert user.source_data["endpoints"]
 
       refute user.last_refreshed_at == orig_user.last_refreshed_at
     end
@@ -611,63 +590,63 @@ defmodule Pleroma.UserTest do
   end
 
   describe "updating note and follower count" do
-    test "it sets the info->note_count property" do
+    test "it sets the note_count property" do
       note = insert(:note)
 
       user = User.get_cached_by_ap_id(note.data["actor"])
 
-      assert user.info.note_count == 0
+      assert user.note_count == 0
 
       {:ok, user} = User.update_note_count(user)
 
-      assert user.info.note_count == 1
+      assert user.note_count == 1
     end
 
-    test "it increases the info->note_count property" do
+    test "it increases the note_count property" do
       note = insert(:note)
       user = User.get_cached_by_ap_id(note.data["actor"])
 
-      assert user.info.note_count == 0
+      assert user.note_count == 0
 
       {:ok, user} = User.increase_note_count(user)
 
-      assert user.info.note_count == 1
+      assert user.note_count == 1
 
       {:ok, user} = User.increase_note_count(user)
 
-      assert user.info.note_count == 2
+      assert user.note_count == 2
     end
 
-    test "it decreases the info->note_count property" do
+    test "it decreases the note_count property" do
       note = insert(:note)
       user = User.get_cached_by_ap_id(note.data["actor"])
 
-      assert user.info.note_count == 0
+      assert user.note_count == 0
 
       {:ok, user} = User.increase_note_count(user)
 
-      assert user.info.note_count == 1
+      assert user.note_count == 1
 
       {:ok, user} = User.decrease_note_count(user)
 
-      assert user.info.note_count == 0
+      assert user.note_count == 0
 
       {:ok, user} = User.decrease_note_count(user)
 
-      assert user.info.note_count == 0
+      assert user.note_count == 0
     end
 
-    test "it sets the info->follower_count property" do
+    test "it sets the follower_count property" do
       user = insert(:user)
       follower = insert(:user)
 
       User.follow(follower, user)
 
-      assert user.info.follower_count == 0
+      assert user.follower_count == 0
 
       {:ok, user} = User.update_follower_count(user)
 
-      assert user.info.follower_count == 1
+      assert user.follower_count == 1
     end
   end
 
@@ -958,14 +937,14 @@ defmodule Pleroma.UserTest do
   describe ".deactivate" do
     test "can de-activate then re-activate a user" do
       user = insert(:user)
-      assert false == user.info.deactivated
+      assert false == user.deactivated
       {:ok, user} = User.deactivate(user)
-      assert true == user.info.deactivated
+      assert true == user.deactivated
       {:ok, user} = User.deactivate(user, false)
-      assert false == user.info.deactivated
+      assert false == user.deactivated
     end
 
-    test "hide a user from followers " do
+    test "hide a user from followers" do
       user = insert(:user)
       user2 = insert(:user)
 
@@ -1041,7 +1020,7 @@ defmodule Pleroma.UserTest do
     end
 
     test "it deletes deactivated user" do
-      {:ok, user} = insert(:user, info: %{deactivated: true}) |> User.set_cache()
+      {:ok, user} = insert(:user, deactivated: true) |> User.set_cache()
 
       {:ok, job} = User.delete(user)
       {:ok, _user} = ObanHelpers.perform(job)
@@ -1132,11 +1111,9 @@ defmodule Pleroma.UserTest do
         ap_id: user.ap_id,
         name: user.name,
         nickname: user.nickname,
-        info: %{
-          fields: [
-            %{"name" => "myfield", "value" => String.duplicate("h", current_max_length + 1)}
-          ]
-        }
+        fields: [
+          %{"name" => "myfield", "value" => String.duplicate("h", current_max_length + 1)}
+        ]
       }
 
       assert {:ok, %User{}} = User.insert_or_update_user(data)
@@ -1180,7 +1157,7 @@ defmodule Pleroma.UserTest do
     end
 
     test "html_filter_policy returns TwitterText scrubber when rich-text is disabled" do
-      user = insert(:user, %{info: %{no_rich_text: true}})
+      user = insert(:user, no_rich_text: true)
 
       assert Pleroma.HTML.Scrubber.TwitterText == User.html_filter_policy(user)
     end
@@ -1217,8 +1194,8 @@ defmodule Pleroma.UserTest do
   test "auth_active?/1 works correctly" do
     Pleroma.Config.put([:instance, :account_activation_required], true)
 
-    local_user = insert(:user, local: true, info: %{confirmation_pending: true})
-    confirmed_user = insert(:user, local: true, info: %{confirmation_pending: false})
+    local_user = insert(:user, local: true, confirmation_pending: true)
+    confirmed_user = insert(:user, local: true, confirmation_pending: false)
     remote_user = insert(:user, local: false)
 
     refute User.auth_active?(local_user)
@@ -1235,25 +1212,39 @@ defmodule Pleroma.UserTest do
 
     test "returns false for remote users" do
       user = insert(:user, local: false)
-      remote_admin_user = insert(:user, local: false, info: %{is_admin: true})
+      remote_admin_user = insert(:user, local: false, is_admin: true)
 
       refute User.superuser?(user)
       refute User.superuser?(remote_admin_user)
     end
 
     test "returns true for local moderators" do
-      user = insert(:user, local: true, info: %{is_moderator: true})
+      user = insert(:user, local: true, is_moderator: true)
 
       assert User.superuser?(user)
     end
 
     test "returns true for local admins" do
-      user = insert(:user, local: true, info: %{is_admin: true})
+      user = insert(:user, local: true, is_admin: true)
 
       assert User.superuser?(user)
     end
   end
 
+  describe "invisible?/1" do
+    test "returns true for an invisible user" do
+      user = insert(:user, local: true, invisible: true)
+
+      assert User.invisible?(user)
+    end
+
+    test "returns false for a non-invisible user" do
+      user = insert(:user, local: true)
+
+      refute User.invisible?(user)
+    end
+  end
+
   describe "visible_for?/2" do
     test "returns true when the account is itself" do
       user = insert(:user, local: true)
@@ -1264,14 +1255,14 @@ defmodule Pleroma.UserTest do
     test "returns false when the account is unauthenticated and auth is required" do
       Pleroma.Config.put([:instance, :account_activation_required], true)
 
-      user = insert(:user, local: true, info: %{confirmation_pending: true})
+      user = insert(:user, local: true, confirmation_pending: true)
       other_user = insert(:user, local: true)
 
       refute User.visible_for?(user, other_user)
     end
 
     test "returns true when the account is unauthenticated and auth is not required" do
-      user = insert(:user, local: true, info: %{confirmation_pending: true})
+      user = insert(:user, local: true, confirmation_pending: true)
       other_user = insert(:user, local: true)
 
       assert User.visible_for?(user, other_user)
@@ -1280,8 +1271,8 @@ defmodule Pleroma.UserTest do
     test "returns true when the account is unauthenticated and being viewed by a privileged account (auth required)" do
       Pleroma.Config.put([:instance, :account_activation_required], true)
 
-      user = insert(:user, local: true, info: %{confirmation_pending: true})
-      other_user = insert(:user, local: true, info: %{is_admin: true})
+      user = insert(:user, local: true, confirmation_pending: true)
+      other_user = insert(:user, local: true, is_admin: true)
 
       assert User.visible_for?(user, other_user)
     end
@@ -1347,7 +1338,7 @@ defmodule Pleroma.UserTest do
 
       users =
         Enum.map(1..total, fn _ ->
-          insert(:user, last_digest_emailed_at: days_ago(20), info: %{deactivated: false})
+          insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false)
         end)
 
       inactive_users_ids =
@@ -1365,7 +1356,7 @@ defmodule Pleroma.UserTest do
 
       users =
         Enum.map(1..total, fn _ ->
-          insert(:user, last_digest_emailed_at: days_ago(20), info: %{deactivated: false})
+          insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false)
         end)
 
       {inactive, active} = Enum.split(users, trunc(total / 2))
@@ -1398,7 +1389,7 @@ defmodule Pleroma.UserTest do
 
       users =
         Enum.map(1..total, fn _ ->
-          insert(:user, last_digest_emailed_at: days_ago(20), info: %{deactivated: false})
+          insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false)
         end)
 
       [sender | recipients] = users
@@ -1438,19 +1429,19 @@ defmodule Pleroma.UserTest do
 
   describe "toggle_confirmation/1" do
     test "if user is confirmed" do
-      user = insert(:user, info: %{confirmation_pending: false})
+      user = insert(:user, confirmation_pending: false)
       {:ok, user} = User.toggle_confirmation(user)
 
-      assert user.info.confirmation_pending
-      assert user.info.confirmation_token
+      assert user.confirmation_pending
+      assert user.confirmation_token
     end
 
     test "if user is unconfirmed" do
-      user = insert(:user, info: %{confirmation_pending: true, confirmation_token: "some token"})
+      user = insert(:user, confirmation_pending: true, confirmation_token: "some token")
       {:ok, user} = User.toggle_confirmation(user)
 
-      refute user.info.confirmation_pending
-      refute user.info.confirmation_token
+      refute user.confirmation_pending
+      refute user.confirmation_token
     end
   end
 
@@ -1486,7 +1477,7 @@ defmodule Pleroma.UserTest do
       user1 = insert(:user, local: false, ap_id: "http://localhost:4001/users/masto_closed")
       user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
       insert(:user, local: true)
-      insert(:user, local: false, info: %{deactivated: true})
+      insert(:user, local: false, deactivated: true)
       {:ok, user1: user1, user2: user2}
     end
 
@@ -1605,7 +1596,7 @@ defmodule Pleroma.UserTest do
           local: false,
           follower_address: "http://localhost:4001/users/masto_closed/followers",
           following_address: "http://localhost:4001/users/masto_closed/following",
-          info: %{ap_enabled: true}
+          ap_enabled: true
         )
 
       assert User.user_info(other_user).following_count == 0
@@ -1628,7 +1619,7 @@ defmodule Pleroma.UserTest do
           local: false,
           follower_address: "http://localhost:4001/users/masto_closed/followers",
           following_address: "http://localhost:4001/users/masto_closed/following",
-          info: %{ap_enabled: true}
+          ap_enabled: true
         )
 
       assert User.user_info(other_user).following_count == 0
@@ -1651,7 +1642,7 @@ defmodule Pleroma.UserTest do
           local: false,
           follower_address: "http://localhost:4001/users/masto_closed/followers",
           following_address: "http://localhost:4001/users/masto_closed/following",
-          info: %{ap_enabled: true}
+          ap_enabled: true
         )
 
       assert User.user_info(other_user).following_count == 0
@@ -1691,41 +1682,6 @@ defmodule Pleroma.UserTest do
     end
   end
 
-  describe "set_password_reset_pending/2" do
-    setup do
-      [user: insert(:user)]
-    end
-
-    test "sets password_reset_pending to true", %{user: user} do
-      %{password_reset_pending: password_reset_pending} = user.info
-
-      refute password_reset_pending
-
-      {:ok, %{info: %{password_reset_pending: password_reset_pending}}} =
-        User.force_password_reset(user)
-
-      assert password_reset_pending
-    end
-  end
-
-  test "change_info/2" do
-    user = insert(:user)
-    assert user.info.hide_follows == false
-
-    changeset = User.change_info(user, &User.Info.profile_update(&1, %{hide_follows: true}))
-    assert changeset.changes.info.changes.hide_follows == true
-  end
-
-  test "update_info/2" do
-    user = insert(:user)
-    assert user.info.hide_follows == false
-
-    assert {:ok, _} = User.update_info(user, &User.Info.profile_update(&1, %{hide_follows: true}))
-
-    assert %{info: %{hide_follows: true}} = Repo.get(User, user.id)
-    assert {:ok, %{info: %{hide_follows: true}}} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
-  end
-
   describe "get_cached_by_nickname_or_id" do
     setup do
       limit_to_local_content = Pleroma.Config.get([:instance, :limit_to_local_content])
@@ -1782,4 +1738,18 @@ defmodule Pleroma.UserTest do
       assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname)
     end
   end
+
+  describe "update_email_notifications/2" do
+    setup do
+      user = insert(:user, email_notifications: %{"digest" => true})
+
+      {:ok, user: user}
+    end
+
+    test "Notifications are updated", %{user: user} do
+      true = user.email_notifications["digest"]
+      assert {:ok, result} = User.update_email_notifications(user, %{"digest" => false})
+      assert result.email_notifications["digest"] == false
+    end
+  end
 end
index 6a3e48b5e7c93f622b98f59854130b102b84ffb1..a5414c5210bc66b6f38c63ce70f14a07416bc6a8 100644 (file)
@@ -354,6 +354,87 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
       assert Activity.get_by_ap_id(data["id"])
     end
 
+    test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do
+      user = insert(:user)
+
+      data =
+        Map.put(data, "to", user.ap_id)
+        |> Map.delete("cc")
+
+      conn =
+        conn
+        |> assign(:valid_signature, true)
+        |> put_req_header("content-type", "application/activity+json")
+        |> post("/users/#{user.nickname}/inbox", data)
+
+      assert "ok" == json_response(conn, 200)
+      ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
+      assert Activity.get_by_ap_id(data["id"])
+    end
+
+    test "it accepts messages with cc as string instead of array", %{conn: conn, data: data} do
+      user = insert(:user)
+
+      data =
+        Map.put(data, "cc", user.ap_id)
+        |> Map.delete("to")
+
+      conn =
+        conn
+        |> assign(:valid_signature, true)
+        |> put_req_header("content-type", "application/activity+json")
+        |> post("/users/#{user.nickname}/inbox", data)
+
+      assert "ok" == json_response(conn, 200)
+      ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
+      %Activity{} = activity = Activity.get_by_ap_id(data["id"])
+      assert user.ap_id in activity.recipients
+    end
+
+    test "it accepts messages with bcc as string instead of array", %{conn: conn, data: data} do
+      user = insert(:user)
+
+      data =
+        Map.put(data, "bcc", user.ap_id)
+        |> Map.delete("to")
+        |> Map.delete("cc")
+
+      conn =
+        conn
+        |> assign(:valid_signature, true)
+        |> put_req_header("content-type", "application/activity+json")
+        |> post("/users/#{user.nickname}/inbox", data)
+
+      assert "ok" == json_response(conn, 200)
+      ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
+      assert Activity.get_by_ap_id(data["id"])
+    end
+
+    test "it accepts announces with to as string instead of array", %{conn: conn} do
+      user = insert(:user)
+
+      data = %{
+        "@context" => "https://www.w3.org/ns/activitystreams",
+        "actor" => "http://mastodon.example.org/users/admin",
+        "id" => "http://mastodon.example.org/users/admin/statuses/19512778738411822/activity",
+        "object" => "https://mastodon.social/users/emelie/statuses/101849165031453009",
+        "to" => "https://www.w3.org/ns/activitystreams#Public",
+        "cc" => [user.ap_id],
+        "type" => "Announce"
+      }
+
+      conn =
+        conn
+        |> assign(:valid_signature, true)
+        |> put_req_header("content-type", "application/activity+json")
+        |> post("/users/#{user.nickname}/inbox", data)
+
+      assert "ok" == json_response(conn, 200)
+      ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
+      %Activity{} = activity = Activity.get_by_ap_id(data["id"])
+      assert "https://www.w3.org/ns/activitystreams#Public" in activity.recipients
+    end
+
     test "it accepts messages from actors that are followed by the user", %{
       conn: conn,
       data: data
@@ -683,7 +764,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
 
     test "it returns returns a uri if the user has 'hide_followers' set", %{conn: conn} do
       user = insert(:user)
-      user_two = insert(:user, %{info: %{hide_followers: true}})
+      user_two = insert(:user, hide_followers: true)
       User.follow(user, user_two)
 
       result =
@@ -696,7 +777,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
 
     test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is not authenticated",
          %{conn: conn} do
-      user = insert(:user, %{info: %{hide_followers: true}})
+      user = insert(:user, hide_followers: true)
 
       result =
         conn
@@ -708,7 +789,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
 
     test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user",
          %{conn: conn} do
-      user = insert(:user, %{info: %{hide_followers: true}})
+      user = insert(:user, hide_followers: true)
       other_user = insert(:user)
       {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
 
@@ -764,7 +845,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
     end
 
     test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do
-      user = insert(:user, %{info: %{hide_follows: true}})
+      user = insert(:user, hide_follows: true)
       user_two = insert(:user)
       User.follow(user, user_two)
 
@@ -778,7 +859,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
 
     test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is not authenticated",
          %{conn: conn} do
-      user = insert(:user, %{info: %{hide_follows: true}})
+      user = insert(:user, hide_follows: true)
 
       result =
         conn
@@ -790,7 +871,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
 
     test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user",
          %{conn: conn} do
-      user = insert(:user, %{info: %{hide_follows: true}})
+      user = insert(:user, hide_follows: true)
       other_user = insert(:user)
       {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
 
index ddc6493c002e2ca50e17b09e0b64aed0aa606de3..e125167225b93ae6fa84458c9cf483e6d15a4d48 100644 (file)
@@ -42,6 +42,27 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
         assert called(Pleroma.Web.Streamer.stream("participation", participations))
       end
     end
+
+    test "streams them out on activity creation" do
+      user_one = insert(:user)
+      user_two = insert(:user)
+
+      with_mock Pleroma.Web.Streamer,
+        stream: fn _, _ -> nil end do
+        {:ok, activity} =
+          CommonAPI.post(user_one, %{
+            "status" => "@#{user_two.nickname}",
+            "visibility" => "direct"
+          })
+
+        conversation =
+          activity.data["context"]
+          |> Pleroma.Conversation.get_for_ap_id()
+          |> Repo.preload(participations: :user)
+
+        assert called(Pleroma.Web.Streamer.stream("participation", conversation.participations))
+      end
+    end
   end
 
   describe "fetching restricted by visibility" do
@@ -154,11 +175,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
       {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
       assert user.ap_id == user_id
       assert user.nickname == "admin@mastodon.example.org"
-      assert user.info.source_data
-      assert user.info.ap_enabled
+      assert user.source_data
+      assert user.ap_enabled
       assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
     end
 
+    test "it returns a user that is invisible" do
+      user_id = "http://mastodon.example.org/users/relay"
+      {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
+      assert User.invisible?(user)
+    end
+
     test "it fetches the appropriate tag-restricted posts" do
       user = insert(:user)
 
@@ -340,7 +367,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
       assert activity.actor == user.ap_id
 
       user = User.get_cached_by_id(user.id)
-      assert user.info.note_count == 0
+      assert user.note_count == 0
     end
 
     test "can be fetched into a timeline" do
@@ -403,7 +430,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
         })
 
       user = User.get_cached_by_id(user.id)
-      assert user.info.note_count == 2
+      assert user.note_count == 2
     end
 
     test "increases replies count" do
@@ -1070,7 +1097,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
     end
 
     test "decrements user note count only for public activities" do
-      user = insert(:user, info: %{note_count: 10})
+      user = insert(:user, note_count: 10)
 
       {:ok, a1} =
         CommonAPI.post(User.get_cached_by_id(user.id), %{
@@ -1102,7 +1129,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
       {:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
 
       user = User.get_cached_by_id(user.id)
-      assert user.info.note_count == 10
+      assert user.note_count == 10
     end
 
     test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
@@ -1430,9 +1457,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
           following_address: "http://localhost:4001/users/masto_closed/following"
         )
 
-      {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
-      assert info.hide_followers == true
-      assert info.hide_follows == false
+      {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
+      assert follow_info.hide_followers == true
+      assert follow_info.hide_follows == false
     end
 
     test "detects hidden follows" do
@@ -1453,9 +1480,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
           following_address: "http://localhost:4001/users/masto_closed/following"
         )
 
-      {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
-      assert info.hide_followers == false
-      assert info.hide_follows == true
+      {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
+      assert follow_info.hide_followers == false
+      assert follow_info.hide_follows == true
     end
   end
 end
index 03dc299ecf7fc3d4bb649956c0b35e295b62c1ff..b524fdd2358c3bc60a9802a82e211d31d8a543f3 100644 (file)
@@ -35,7 +35,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
     test "it allows posts without links" do
       user = insert(:user)
 
-      assert user.info.note_count == 0
+      assert user.note_count == 0
 
       message =
         @linkless_message
@@ -47,7 +47,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
     test "it disallows posts with links" do
       user = insert(:user)
 
-      assert user.info.note_count == 0
+      assert user.note_count == 0
 
       message =
         @linkful_message
@@ -59,9 +59,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
 
   describe "with old user" do
     test "it allows posts without links" do
-      user = insert(:user, info: %{note_count: 1})
+      user = insert(:user, note_count: 1)
 
-      assert user.info.note_count == 1
+      assert user.note_count == 1
 
       message =
         @linkless_message
@@ -71,9 +71,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
     end
 
     test "it allows posts with links" do
-      user = insert(:user, info: %{note_count: 1})
+      user = insert(:user, note_count: 1)
 
-      assert user.info.note_count == 1
+      assert user.note_count == 1
 
       message =
         @linkful_message
@@ -85,9 +85,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
 
   describe "with followed new user" do
     test "it allows posts without links" do
-      user = insert(:user, info: %{follower_count: 1})
+      user = insert(:user, follower_count: 1)
 
-      assert user.info.follower_count == 1
+      assert user.follower_count == 1
 
       message =
         @linkless_message
@@ -97,9 +97,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
     end
 
     test "it allows posts with links" do
-      user = insert(:user, info: %{follower_count: 1})
+      user = insert(:user, follower_count: 1)
 
-      assert user.info.follower_count == 1
+      assert user.follower_count == 1
 
       message =
         @linkful_message
@@ -133,7 +133,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
 
   describe "with contentless-objects" do
     test "it does not reject them or error out" do
-      user = insert(:user, info: %{note_count: 1})
+      user = insert(:user, note_count: 1)
 
       message =
         @response_message
index df03b40087aa708036eb6568f2a58522b899e150..e885e5a5abb87bde31302f93562e97e6555d4cb7 100644 (file)
@@ -27,7 +27,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
     test "it returns sharedInbox for messages involving as:Public in to" do
       user =
         insert(:user, %{
-          info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
+          source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}
         })
 
       activity = %Activity{
@@ -40,7 +40,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
     test "it returns sharedInbox for messages involving as:Public in cc" do
       user =
         insert(:user, %{
-          info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
+          source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}
         })
 
       activity = %Activity{
@@ -53,7 +53,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
     test "it returns sharedInbox for messages involving multiple recipients in to" do
       user =
         insert(:user, %{
-          info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
+          source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}
         })
 
       user_two = insert(:user)
@@ -69,7 +69,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
     test "it returns sharedInbox for messages involving multiple recipients in cc" do
       user =
         insert(:user, %{
-          info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
+          source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}
         })
 
       user_two = insert(:user)
@@ -84,14 +84,12 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
 
     test "it returns sharedInbox for messages involving multiple recipients in total" do
       user =
-        insert(:user, %{
-          info: %{
-            source_data: %{
-              "inbox" => "http://example.com/personal-inbox",
-              "endpoints" => %{"sharedInbox" => "http://example.com/inbox"}
-            }
+        insert(:user,
+          source_data: %{
+            "inbox" => "http://example.com/personal-inbox",
+            "endpoints" => %{"sharedInbox" => "http://example.com/inbox"}
           }
-        })
+        )
 
       user_two = insert(:user)
 
@@ -104,14 +102,12 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
 
     test "it returns inbox for messages involving single recipients in total" do
       user =
-        insert(:user, %{
-          info: %{
-            source_data: %{
-              "inbox" => "http://example.com/personal-inbox",
-              "endpoints" => %{"sharedInbox" => "http://example.com/inbox"}
-            }
+        insert(:user,
+          source_data: %{
+            "inbox" => "http://example.com/personal-inbox",
+            "endpoints" => %{"sharedInbox" => "http://example.com/inbox"}
           }
-        })
+        )
 
       activity = %Activity{
         data: %{"to" => [user.ap_id], "cc" => []}
@@ -241,10 +237,8 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
       follower =
         insert(:user,
           local: false,
-          info: %{
-            ap_enabled: true,
-            source_data: %{"inbox" => "https://domain.com/users/nick1/inbox"}
-          }
+          source_data: %{"inbox" => "https://domain.com/users/nick1/inbox"},
+          ap_enabled: true
         )
 
       actor = insert(:user, follower_address: follower.ap_id)
@@ -278,19 +272,15 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
       fetcher =
         insert(:user,
           local: false,
-          info: %{
-            ap_enabled: true,
-            source_data: %{"inbox" => "https://domain.com/users/nick1/inbox"}
-          }
+          source_data: %{"inbox" => "https://domain.com/users/nick1/inbox"},
+          ap_enabled: true
         )
 
       another_fetcher =
         insert(:user,
           local: false,
-          info: %{
-            ap_enabled: true,
-            source_data: %{"inbox" => "https://domain2.com/users/nick1/inbox"}
-          }
+          source_data: %{"inbox" => "https://domain2.com/users/nick1/inbox"},
+          ap_enabled: true
         )
 
       actor = insert(:user)
index 0f7556538651c8c890d86595bb40502b2556f250..ac2007b2cee0ff91ef8496e438e681411d656c2e 100644 (file)
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
 
   alias Pleroma.Activity
   alias Pleroma.Object
+  alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.Relay
 
@@ -19,11 +20,16 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
     assert user.ap_id == "#{Pleroma.Web.Endpoint.url()}/relay"
   end
 
+  test "relay actor is invisible" do
+    user = Relay.get_actor()
+    assert User.invisible?(user)
+  end
+
   describe "follow/1" do
     test "returns errors when user not found" do
       assert capture_log(fn ->
-               assert Relay.follow("test-ap-id") == {:error, "Could not fetch by AP id"}
-             end) =~ "Could not fetch by AP id"
+               {:error, _} = Relay.follow("test-ap-id")
+             end) =~ "Could not decode user at fetch"
     end
 
     test "returns activity" do
@@ -41,8 +47,8 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
   describe "unfollow/1" do
     test "returns errors when user not found" do
       assert capture_log(fn ->
-               assert Relay.unfollow("test-ap-id") == {:error, "Could not fetch by AP id"}
-             end) =~ "Could not fetch by AP id"
+               {:error, _} = Relay.unfollow("test-ap-id")
+             end) =~ "Could not decode user at fetch"
     end
 
     test "returns activity" do
index 99ab573c518f2556954914a2be099e959c7424fe..75cfbea2e284195811b4e18eba5a9bb6bf89ea29 100644 (file)
@@ -58,7 +58,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do
     end
 
     test "with locked accounts, it does not create a follow or an accept" do
-      user = insert(:user, info: %{locked: true})
+      user = insert(:user, locked: true)
 
       data =
         File.read!("test/fixtures/mastodon-follow-activity.json")
index 82389b344543dfbed9a5e6f78d2a03b7393eb6d1..1069ade02a0e8751ba11502b1c6a6fd696073265 100644 (file)
@@ -7,15 +7,12 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
   alias Pleroma.Activity
   alias Pleroma.Object
   alias Pleroma.Object.Fetcher
-  alias Pleroma.Repo
   alias Pleroma.Tests.ObanHelpers
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.Transmogrifier
   alias Pleroma.Web.AdminAPI.AccountView
   alias Pleroma.Web.CommonAPI
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.Websub.WebsubClientSubscription
 
   import Mock
   import Pleroma.Factory
@@ -149,7 +146,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       user = User.get_cached_by_ap_id(object_data["actor"])
 
-      assert user.info.note_count == 1
+      assert user.note_count == 1
     end
 
     test "it works for incoming notices with hashtags" do
@@ -586,7 +583,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
                }
              ]
 
-      assert user.info.banner["url"] == [
+      assert user.banner["url"] == [
                %{
                  "href" =>
                    "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
@@ -605,7 +602,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       user = User.get_cached_by_ap_id(activity.actor)
 
-      assert User.Info.fields(user.info) == [
+      assert User.fields(user) == [
                %{"name" => "foo", "value" => "bar"},
                %{"name" => "foo1", "value" => "bar1"}
              ]
@@ -626,7 +623,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       user = User.get_cached_by_ap_id(user.ap_id)
 
-      assert User.Info.fields(user.info) == [
+      assert User.fields(user) == [
                %{"name" => "foo", "value" => "updated"},
                %{"name" => "foo1", "value" => "updated"}
              ]
@@ -644,7 +641,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       user = User.get_cached_by_ap_id(user.ap_id)
 
-      assert User.Info.fields(user.info) == [
+      assert User.fields(user) == [
                %{"name" => "foo", "value" => "updated"},
                %{"name" => "foo1", "value" => "updated"}
              ]
@@ -655,7 +652,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       user = User.get_cached_by_ap_id(user.ap_id)
 
-      assert User.Info.fields(user.info) == []
+      assert User.fields(user) == []
     end
 
     test "it works for incoming update activities which lock the account" do
@@ -678,7 +675,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
 
       user = User.get_cached_by_ap_id(data["actor"])
-      assert user.info.locked == true
+      assert user.locked == true
     end
 
     test "it works for incoming deletes" do
@@ -724,7 +721,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert capture_log(fn ->
                :error = Transmogrifier.handle_incoming(data)
              end) =~
-               "[error] Could not decode user at fetch http://mastodon.example.org/users/gargron, {:error, {:error, :nxdomain}}"
+               "[error] Could not decode user at fetch http://mastodon.example.org/users/gargron, {:error, :nxdomain}"
 
       assert Activity.get_by_id(activity.id)
     end
@@ -919,7 +916,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
     test "it works for incoming accepts which were orphaned" do
       follower = insert(:user)
-      followed = insert(:user, %{info: %User.Info{locked: true}})
+      followed = insert(:user, locked: true)
 
       {:ok, follow_activity} = ActivityPub.follow(follower, followed)
 
@@ -941,7 +938,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
     test "it works for incoming accepts which are referenced by IRI only" do
       follower = insert(:user)
-      followed = insert(:user, %{info: %User.Info{locked: true}})
+      followed = insert(:user, locked: true)
 
       {:ok, follow_activity} = ActivityPub.follow(follower, followed)
 
@@ -961,7 +958,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
     test "it fails for incoming accepts which cannot be correlated" do
       follower = insert(:user)
-      followed = insert(:user, %{info: %User.Info{locked: true}})
+      followed = insert(:user, locked: true)
 
       accept_data =
         File.read!("test/fixtures/mastodon-accept-activity.json")
@@ -980,7 +977,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
     test "it fails for incoming rejects which cannot be correlated" do
       follower = insert(:user)
-      followed = insert(:user, %{info: %User.Info{locked: true}})
+      followed = insert(:user, locked: true)
 
       accept_data =
         File.read!("test/fixtures/mastodon-reject-activity.json")
@@ -999,7 +996,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
     test "it works for incoming rejects which are orphaned" do
       follower = insert(:user)
-      followed = insert(:user, %{info: %User.Info{locked: true}})
+      followed = insert(:user, locked: true)
 
       {:ok, follower} = User.follow(follower, followed)
       {:ok, _follow_activity} = ActivityPub.follow(follower, followed)
@@ -1025,7 +1022,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
     test "it works for incoming rejects which are referenced by IRI only" do
       follower = insert(:user)
-      followed = insert(:user, %{info: %User.Info{locked: true}})
+      followed = insert(:user, locked: true)
 
       {:ok, follower} = User.follow(follower, followed)
       {:ok, follow_activity} = ActivityPub.follow(follower, followed)
@@ -1118,6 +1115,50 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert activity.data["actor"] == other_user.ap_id
       assert activity.data["cc"] == [user.ap_id]
     end
+
+    test "it correctly processes messages with non-array to field" do
+      user = insert(:user)
+
+      message = %{
+        "@context" => "https://www.w3.org/ns/activitystreams",
+        "to" => "https://www.w3.org/ns/activitystreams#Public",
+        "type" => "Create",
+        "object" => %{
+          "content" => "blah blah blah",
+          "type" => "Note",
+          "attributedTo" => user.ap_id,
+          "inReplyTo" => nil
+        },
+        "actor" => user.ap_id
+      }
+
+      assert {:ok, activity} = Transmogrifier.handle_incoming(message)
+
+      assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"]
+    end
+
+    test "it correctly processes messages with non-array cc field" do
+      user = insert(:user)
+
+      message = %{
+        "@context" => "https://www.w3.org/ns/activitystreams",
+        "to" => user.follower_address,
+        "cc" => "https://www.w3.org/ns/activitystreams#Public",
+        "type" => "Create",
+        "object" => %{
+          "content" => "blah blah blah",
+          "type" => "Note",
+          "attributedTo" => user.ap_id,
+          "inReplyTo" => nil
+        },
+        "actor" => user.ap_id
+      }
+
+      assert {:ok, activity} = Transmogrifier.handle_incoming(message)
+
+      assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
+      assert [user.follower_address] == activity.data["to"]
+    end
   end
 
   describe "prepare outgoing" do
@@ -1190,32 +1231,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert modified["object"]["actor"] == modified["object"]["attributedTo"]
     end
 
-    test "it translates ostatus IDs to external URLs" do
-      incoming = File.read!("test/fixtures/incoming_note_activity.xml")
-      {:ok, [referent_activity]} = OStatus.handle_incoming(incoming)
-
-      user = insert(:user)
-
-      {:ok, activity, _} = CommonAPI.favorite(referent_activity.id, user)
-      {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
-
-      assert modified["object"] == "http://gs.example.org:4040/index.php/notice/29"
-    end
-
-    test "it translates ostatus reply_to IDs to external URLs" do
-      incoming = File.read!("test/fixtures/incoming_note_activity.xml")
-      {:ok, [referred_activity]} = OStatus.handle_incoming(incoming)
-
-      user = insert(:user)
-
-      {:ok, activity} =
-        CommonAPI.post(user, %{"status" => "HI!", "in_reply_to_status_id" => referred_activity.id})
-
-      {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
-
-      assert modified["object"]["inReplyTo"] == "http://gs.example.org:4040/index.php/notice/29"
-    end
-
     test "it strips internal hashtag data" do
       user = insert(:user)
 
@@ -1335,18 +1350,18 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
 
       user = User.get_cached_by_id(user.id)
-      assert user.info.note_count == 1
+      assert user.note_count == 1
 
       {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
       ObanHelpers.perform_all()
 
-      assert user.info.ap_enabled
-      assert user.info.note_count == 1
+      assert user.ap_enabled
+      assert user.note_count == 1
       assert user.follower_address == "https://niu.moe/users/rye/followers"
       assert user.following_address == "https://niu.moe/users/rye/following"
 
       user = User.get_cached_by_id(user.id)
-      assert user.info.note_count == 1
+      assert user.note_count == 1
 
       activity = Activity.get_by_id(activity.id)
       assert user.follower_address in activity.recipients
@@ -1367,7 +1382,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
                      "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
                  }
                ]
-             } = user.info.banner
+             } = user.banner
 
       refute "..." in activity.recipients
 
@@ -1380,21 +1395,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
     end
   end
 
-  describe "maybe_retire_websub" do
-    test "it deletes all websub client subscripitions with the user as topic" do
-      subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/rye.atom"}
-      {:ok, ws} = Repo.insert(subscription)
-
-      subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/pasty.atom"}
-      {:ok, ws2} = Repo.insert(subscription)
-
-      Transmogrifier.maybe_retire_websub("https://niu.moe/users/rye")
-
-      refute Repo.get(WebsubClientSubscription, ws.id)
-      assert Repo.get(WebsubClientSubscription, ws2.id)
-    end
-  end
-
   describe "actor rewriting" do
     test "it fixes the actor URL property to be a proper URI" do
       data = %{
index 4475d06d22905492507d92fc74b0b2bce3db3583..586eb1d2f902bf11b83e9be7d2d2af942f68aa5d 100644 (file)
@@ -298,7 +298,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
 
   describe "update_follow_state_for_all/2" do
     test "updates the state of all Follow activities with the same actor and object" do
-      user = insert(:user, info: %{locked: true})
+      user = insert(:user, locked: true)
       follower = insert(:user)
 
       {:ok, follow_activity} = ActivityPub.follow(follower, user)
@@ -322,7 +322,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
 
   describe "update_follow_state/2" do
     test "updates the state of the given follow activity" do
-      user = insert(:user, info: %{locked: true})
+      user = insert(:user, locked: true)
       follower = insert(:user)
 
       {:ok, follow_activity} = ActivityPub.follow(follower, user)
index 3155749aac87c9f21504b80763e89675a987e951..3299be2d51530b1503ac6415f5f112128271b298 100644 (file)
@@ -29,7 +29,7 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
 
     {:ok, user} =
       insert(:user)
-      |> User.upgrade_changeset(%{info: %{fields: fields}})
+      |> User.upgrade_changeset(%{fields: fields})
       |> User.update_and_set_cache()
 
     assert %{
@@ -38,7 +38,7 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
   end
 
   test "Renders with emoji tags" do
-    user = insert(:user, %{info: %{emoji: [%{"bib" => "/test"}]}})
+    user = insert(:user, emoji: [%{"bib" => "/test"}])
 
     assert %{
              "tag" => [
@@ -64,9 +64,7 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
     user =
       insert(:user,
         avatar: %{"url" => [%{"href" => "https://someurl"}]},
-        info: %{
-          banner: %{"url" => [%{"href" => "https://somebanner"}]}
-        }
+        banner: %{"url" => [%{"href" => "https://somebanner"}]}
       )
 
     {:ok, user} = User.ensure_keys_present(user)
@@ -76,6 +74,12 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
     assert result["image"]["url"] == "https://somebanner"
   end
 
+  test "renders an invisible user with the invisible property set to true" do
+    user = insert(:user, invisible: true)
+
+    assert %{"invisible" => true} = UserView.render("service.json", %{user: user})
+  end
+
   describe "endpoints" do
     test "local users have a usable endpoints structure" do
       user = insert(:user)
@@ -121,8 +125,7 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
       other_user = insert(:user)
       {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
       assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user})
-      info = Map.merge(user.info, %{hide_followers_count: true, hide_followers: true})
-      user = Map.put(user, :info, info)
+      user = Map.merge(user, %{hide_followers_count: true, hide_followers: true})
       assert %{"totalItems" => 0} = UserView.render("followers.json", %{user: user})
     end
 
@@ -131,8 +134,7 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
       other_user = insert(:user)
       {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
       assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user})
-      info = Map.merge(user.info, %{hide_followers_count: false, hide_followers: true})
-      user = Map.put(user, :info, info)
+      user = Map.merge(user, %{hide_followers_count: false, hide_followers: true})
       assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user})
     end
   end
@@ -143,8 +145,7 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
       other_user = insert(:user)
       {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
       assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
-      info = Map.merge(user.info, %{hide_follows_count: true, hide_follows: true})
-      user = Map.put(user, :info, info)
+      user = Map.merge(user, %{hide_follows_count: true, hide_follows: true})
       assert %{"totalItems" => 0} = UserView.render("following.json", %{user: user})
     end
 
@@ -153,8 +154,7 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
       other_user = insert(:user)
       {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
       assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
-      info = Map.merge(user.info, %{hide_follows_count: false, hide_follows: true})
-      user = Map.put(user, :info, info)
+      user = Map.merge(user, %{hide_follows_count: false, hide_follows: true})
       assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
     end
   end
index b5c355e66f3eb9c36f6ad41bf676306f8f518c26..58435d23cea519ce0bc69f5470a0960df4ac2579 100644 (file)
@@ -17,9 +17,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
   alias Pleroma.Web.MediaProxy
   import Pleroma.Factory
 
-  describe "/api/pleroma/admin/users" do
-    test "Delete" do
-      admin = insert(:user, info: %{is_admin: true})
+  setup_all do
+    Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+
+    :ok
+  end
+
+  describe "DELETE /api/pleroma/admin/users" do
+    test "single user" do
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
 
       conn =
@@ -30,17 +36,38 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
       log_entry = Repo.one(ModerationLog)
 
-      assert log_entry.data["subject"]["nickname"] == user.nickname
-      assert log_entry.data["action"] == "delete"
-
       assert ModerationLog.get_log_entry_message(log_entry) ==
-               "@#{admin.nickname} deleted user @#{user.nickname}"
+               "@#{admin.nickname} deleted users: @#{user.nickname}"
 
       assert json_response(conn, 200) == user.nickname
     end
 
+    test "multiple users" do
+      admin = insert(:user, is_admin: true)
+      user_one = insert(:user)
+      user_two = insert(:user)
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> put_req_header("accept", "application/json")
+        |> delete("/api/pleroma/admin/users", %{
+          nicknames: [user_one.nickname, user_two.nickname]
+        })
+
+      log_entry = Repo.one(ModerationLog)
+
+      assert ModerationLog.get_log_entry_message(log_entry) ==
+               "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
+
+      response = json_response(conn, 200)
+      assert response -- [user_one.nickname, user_two.nickname] == []
+    end
+  end
+
+  describe "/api/pleroma/admin/users" do
     test "Create" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       conn =
         build_conn()
@@ -70,7 +97,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "Cannot create user with exisiting email" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
 
       conn =
@@ -101,7 +128,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "Cannot create user with exisiting nickname" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
 
       conn =
@@ -132,7 +159,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "Multiple user creation works in transaction" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
 
       conn =
@@ -181,7 +208,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "/api/pleroma/admin/users/:nickname" do
     test "Show", %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
 
       conn =
@@ -204,7 +231,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "when the user doesn't exist", %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = build(:user)
 
       conn =
@@ -218,7 +245,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "/api/pleroma/admin/users/follow" do
     test "allows to force-follow another user" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
       follower = insert(:user)
 
@@ -244,7 +271,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "/api/pleroma/admin/users/unfollow" do
     test "allows to force-unfollow another user" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
       follower = insert(:user)
 
@@ -272,7 +299,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "PUT /api/pleroma/admin/users/tag" do
     setup do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user1 = insert(:user, %{tags: ["x"]})
       user2 = insert(:user, %{tags: ["y"]})
       user3 = insert(:user, %{tags: ["unchanged"]})
@@ -321,7 +348,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "DELETE /api/pleroma/admin/users/tag" do
     setup do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user1 = insert(:user, %{tags: ["x"]})
       user2 = insert(:user, %{tags: ["y", "z"]})
       user3 = insert(:user, %{tags: ["unchanged"]})
@@ -370,7 +397,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "/api/pleroma/admin/users/:nickname/permission_group" do
     test "GET is giving user_info" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       conn =
         build_conn()
@@ -385,7 +412,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "/:right POST, can add to a permission group" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
 
       conn =
@@ -404,88 +431,78 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "@#{admin.nickname} made @#{user.nickname} admin"
     end
 
-    test "/:right DELETE, can remove from a permission group" do
-      admin = insert(:user, info: %{is_admin: true})
-      user = insert(:user, info: %{is_admin: true})
+    test "/:right POST, can add to a permission group (multiple)" do
+      admin = insert(:user, is_admin: true)
+      user_one = insert(:user)
+      user_two = insert(:user)
 
       conn =
         build_conn()
         |> assign(:user, admin)
         |> put_req_header("accept", "application/json")
-        |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
+        |> post("/api/pleroma/admin/users/permission_group/admin", %{
+          nicknames: [user_one.nickname, user_two.nickname]
+        })
 
       assert json_response(conn, 200) == %{
-               "is_admin" => false
+               "is_admin" => true
              }
 
       log_entry = Repo.one(ModerationLog)
 
       assert ModerationLog.get_log_entry_message(log_entry) ==
-               "@#{admin.nickname} revoked admin role from @#{user.nickname}"
+               "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
     end
-  end
 
-  describe "PUT /api/pleroma/admin/users/:nickname/activation_status" do
-    setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+    test "/:right DELETE, can remove from a permission group" do
+      admin = insert(:user, is_admin: true)
+      user = insert(:user, is_admin: true)
 
       conn =
-        conn
+        build_conn()
         |> assign(:user, admin)
         |> put_req_header("accept", "application/json")
+        |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
 
-      %{conn: conn, admin: admin}
-    end
-
-    test "deactivates the user", %{conn: conn, admin: admin} do
-      user = insert(:user)
-
-      conn =
-        conn
-        |> put("/api/pleroma/admin/users/#{user.nickname}/activation_status", %{status: false})
-
-      user = User.get_cached_by_id(user.id)
-      assert user.info.deactivated == true
-      assert json_response(conn, :no_content)
+      assert json_response(conn, 200) == %{
+               "is_admin" => false
+             }
 
       log_entry = Repo.one(ModerationLog)
 
       assert ModerationLog.get_log_entry_message(log_entry) ==
-               "@#{admin.nickname} deactivated user @#{user.nickname}"
+               "@#{admin.nickname} revoked admin role from @#{user.nickname}"
     end
 
-    test "activates the user", %{conn: conn, admin: admin} do
-      user = insert(:user, info: %{deactivated: true})
+    test "/:right DELETE, can remove from a permission group (multiple)" do
+      admin = insert(:user, is_admin: true)
+      user_one = insert(:user, is_admin: true)
+      user_two = insert(:user, is_admin: true)
 
       conn =
-        conn
-        |> put("/api/pleroma/admin/users/#{user.nickname}/activation_status", %{status: true})
+        build_conn()
+        |> assign(:user, admin)
+        |> put_req_header("accept", "application/json")
+        |> delete("/api/pleroma/admin/users/permission_group/admin", %{
+          nicknames: [user_one.nickname, user_two.nickname]
+        })
 
-      user = User.get_cached_by_id(user.id)
-      assert user.info.deactivated == false
-      assert json_response(conn, :no_content)
+      assert json_response(conn, 200) == %{
+               "is_admin" => false
+             }
 
       log_entry = Repo.one(ModerationLog)
 
       assert ModerationLog.get_log_entry_message(log_entry) ==
-               "@#{admin.nickname} activated user @#{user.nickname}"
-    end
-
-    test "returns 403 when requested by a non-admin", %{conn: conn} do
-      user = insert(:user)
-
-      conn =
-        conn
-        |> assign(:user, user)
-        |> put("/api/pleroma/admin/users/#{user.nickname}/activation_status", %{status: false})
-
-      assert json_response(conn, :forbidden)
+               "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
+                 user_two.nickname
+               }"
     end
   end
 
   describe "POST /api/pleroma/admin/email_invite, with valid config" do
     setup do
-      [user: insert(:user, info: %{is_admin: true})]
+      [user: insert(:user, is_admin: true)]
     end
 
     clear_config([:instance, :registrations_open]) do
@@ -545,7 +562,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
     setup do
-      [user: insert(:user, info: %{is_admin: true})]
+      [user: insert(:user, is_admin: true)]
     end
 
     clear_config([:instance, :registrations_open])
@@ -577,7 +594,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
   end
 
   test "/api/pleroma/admin/users/:nickname/password_reset" do
-    admin = insert(:user, info: %{is_admin: true})
+    admin = insert(:user, is_admin: true)
     user = insert(:user)
 
     conn =
@@ -593,7 +610,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "GET /api/pleroma/admin/users" do
     setup do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       conn =
         build_conn()
@@ -609,7 +626,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       users =
         [
           %{
-            "deactivated" => admin.info.deactivated,
+            "deactivated" => admin.deactivated,
             "id" => admin.id,
             "nickname" => admin.nickname,
             "roles" => %{"admin" => true, "moderator" => false},
@@ -619,7 +636,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
             "display_name" => HTML.strip_tags(admin.name || admin.nickname)
           },
           %{
-            "deactivated" => user.info.deactivated,
+            "deactivated" => user.deactivated,
             "id" => user.id,
             "nickname" => user.nickname,
             "roles" => %{"admin" => false, "moderator" => false},
@@ -660,7 +677,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 50,
                "users" => [
                  %{
-                   "deactivated" => user.info.deactivated,
+                   "deactivated" => user.deactivated,
                    "id" => user.id,
                    "nickname" => user.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -684,7 +701,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 50,
                "users" => [
                  %{
-                   "deactivated" => user.info.deactivated,
+                   "deactivated" => user.deactivated,
                    "id" => user.id,
                    "nickname" => user.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -708,7 +725,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 50,
                "users" => [
                  %{
-                   "deactivated" => user.info.deactivated,
+                   "deactivated" => user.deactivated,
                    "id" => user.id,
                    "nickname" => user.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -732,7 +749,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 50,
                "users" => [
                  %{
-                   "deactivated" => user.info.deactivated,
+                   "deactivated" => user.deactivated,
                    "id" => user.id,
                    "nickname" => user.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -756,7 +773,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 50,
                "users" => [
                  %{
-                   "deactivated" => user.info.deactivated,
+                   "deactivated" => user.deactivated,
                    "id" => user.id,
                    "nickname" => user.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -780,7 +797,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 1,
                "users" => [
                  %{
-                   "deactivated" => user.info.deactivated,
+                   "deactivated" => user.deactivated,
                    "id" => user.id,
                    "nickname" => user.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -799,7 +816,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 1,
                "users" => [
                  %{
-                   "deactivated" => user2.info.deactivated,
+                   "deactivated" => user2.deactivated,
                    "id" => user2.id,
                    "nickname" => user2.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -813,7 +830,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "only local users" do
-      admin = insert(:user, info: %{is_admin: true}, nickname: "john")
+      admin = insert(:user, is_admin: true, nickname: "john")
       user = insert(:user, nickname: "bob")
 
       insert(:user, nickname: "bobb", local: false)
@@ -828,7 +845,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 50,
                "users" => [
                  %{
-                   "deactivated" => user.info.deactivated,
+                   "deactivated" => user.deactivated,
                    "id" => user.id,
                    "nickname" => user.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -842,7 +859,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "only local users with no query", %{admin: old_admin} do
-      admin = insert(:user, info: %{is_admin: true}, nickname: "john")
+      admin = insert(:user, is_admin: true, nickname: "john")
       user = insert(:user, nickname: "bob")
 
       insert(:user, nickname: "bobb", local: false)
@@ -855,7 +872,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       users =
         [
           %{
-            "deactivated" => user.info.deactivated,
+            "deactivated" => user.deactivated,
             "id" => user.id,
             "nickname" => user.nickname,
             "roles" => %{"admin" => false, "moderator" => false},
@@ -865,7 +882,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
             "display_name" => HTML.strip_tags(user.name || user.nickname)
           },
           %{
-            "deactivated" => admin.info.deactivated,
+            "deactivated" => admin.deactivated,
             "id" => admin.id,
             "nickname" => admin.nickname,
             "roles" => %{"admin" => true, "moderator" => false},
@@ -895,7 +912,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "load only admins", %{conn: conn, admin: admin} do
-      second_admin = insert(:user, info: %{is_admin: true})
+      second_admin = insert(:user, is_admin: true)
       insert(:user)
       insert(:user)
 
@@ -934,7 +951,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "load only moderators", %{conn: conn} do
-      moderator = insert(:user, info: %{is_moderator: true})
+      moderator = insert(:user, is_moderator: true)
       insert(:user)
       insert(:user)
 
@@ -999,11 +1016,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "it works with multiple filters" do
-      admin = insert(:user, nickname: "john", info: %{is_admin: true})
-      user = insert(:user, nickname: "bob", local: false, info: %{deactivated: true})
+      admin = insert(:user, nickname: "john", is_admin: true)
+      user = insert(:user, nickname: "bob", local: false, deactivated: true)
 
-      insert(:user, nickname: "ken", local: true, info: %{deactivated: true})
-      insert(:user, nickname: "bobb", local: false, info: %{deactivated: false})
+      insert(:user, nickname: "ken", local: true, deactivated: true)
+      insert(:user, nickname: "bobb", local: false, deactivated: false)
 
       conn =
         build_conn()
@@ -1015,7 +1032,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 50,
                "users" => [
                  %{
-                   "deactivated" => user.info.deactivated,
+                   "deactivated" => user.deactivated,
                    "id" => user.id,
                    "nickname" => user.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -1029,8 +1046,52 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
   end
 
+  test "PATCH /api/pleroma/admin/users/activate" do
+    admin = insert(:user, is_admin: true)
+    user_one = insert(:user, deactivated: true)
+    user_two = insert(:user, deactivated: true)
+
+    conn =
+      build_conn()
+      |> assign(:user, admin)
+      |> patch(
+        "/api/pleroma/admin/users/activate",
+        %{nicknames: [user_one.nickname, user_two.nickname]}
+      )
+
+    response = json_response(conn, 200)
+    assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
+
+    log_entry = Repo.one(ModerationLog)
+
+    assert ModerationLog.get_log_entry_message(log_entry) ==
+             "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
+  end
+
+  test "PATCH /api/pleroma/admin/users/deactivate" do
+    admin = insert(:user, is_admin: true)
+    user_one = insert(:user, deactivated: false)
+    user_two = insert(:user, deactivated: false)
+
+    conn =
+      build_conn()
+      |> assign(:user, admin)
+      |> patch(
+        "/api/pleroma/admin/users/deactivate",
+        %{nicknames: [user_one.nickname, user_two.nickname]}
+      )
+
+    response = json_response(conn, 200)
+    assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
+
+    log_entry = Repo.one(ModerationLog)
+
+    assert ModerationLog.get_log_entry_message(log_entry) ==
+             "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
+  end
+
   test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do
-    admin = insert(:user, info: %{is_admin: true})
+    admin = insert(:user, is_admin: true)
     user = insert(:user)
 
     conn =
@@ -1040,7 +1101,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
     assert json_response(conn, 200) ==
              %{
-               "deactivated" => !user.info.deactivated,
+               "deactivated" => !user.deactivated,
                "id" => user.id,
                "nickname" => user.nickname,
                "roles" => %{"admin" => false, "moderator" => false},
@@ -1053,12 +1114,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     log_entry = Repo.one(ModerationLog)
 
     assert ModerationLog.get_log_entry_message(log_entry) ==
-             "@#{admin.nickname} deactivated user @#{user.nickname}"
+             "@#{admin.nickname} deactivated users: @#{user.nickname}"
   end
 
   describe "POST /api/pleroma/admin/users/invite_token" do
     setup do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       conn =
         build_conn()
@@ -1122,7 +1183,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "GET /api/pleroma/admin/users/invites" do
     setup do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       conn =
         build_conn()
@@ -1160,7 +1221,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "POST /api/pleroma/admin/users/revoke_invite" do
     test "with token" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       {:ok, invite} = UserInviteToken.create_invite()
 
       conn =
@@ -1180,7 +1241,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "with invalid token" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       conn =
         build_conn()
@@ -1193,7 +1254,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "GET /api/pleroma/admin/reports/:id" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       %{conn: assign(conn, :user, admin)}
     end
@@ -1226,7 +1287,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "PUT /api/pleroma/admin/reports/:id" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       [reporter, target_user] = insert_pair(:user)
       activity = insert(:note_activity, user: target_user)
 
@@ -1287,7 +1348,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "GET /api/pleroma/admin/reports" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       %{conn: assign(conn, :user, admin)}
     end
@@ -1407,7 +1468,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
   #
   describe "POST /api/pleroma/admin/reports/:id/respond" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       %{conn: assign(conn, :user, admin), admin: admin}
     end
@@ -1462,7 +1523,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "PUT /api/pleroma/admin/statuses/:id" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       activity = insert(:note_activity)
 
       %{conn: assign(conn, :user, admin), id: activity.id, admin: admin}
@@ -1528,7 +1589,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "DELETE /api/pleroma/admin/statuses/:id" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       activity = insert(:note_activity)
 
       %{conn: assign(conn, :user, admin), id: activity.id, admin: admin}
@@ -1558,7 +1619,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "GET /api/pleroma/admin/config" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       %{conn: assign(conn, :user, admin)}
     end
@@ -1595,7 +1656,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "POST /api/pleroma/admin/config" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       temp_file = "config/test.exported_from_db.secret.exs"
 
@@ -2163,7 +2224,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "config mix tasks run" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       temp_file = "config/test.exported_from_db.secret.exs"
 
@@ -2199,7 +2260,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "GET /api/pleroma/admin/users/:nickname/statuses" do
     setup do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
 
       date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
@@ -2256,8 +2317,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "GET /api/pleroma/admin/moderation_log" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
-      moderator = insert(:user, info: %{is_moderator: true})
+      admin = insert(:user, is_admin: true)
+      moderator = insert(:user, is_moderator: true)
 
       %{conn: assign(conn, :user, admin), admin: admin, moderator: moderator}
     end
@@ -2465,14 +2526,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "PATCH /users/:nickname/force_password_reset" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
 
       %{conn: assign(conn, :user, admin), admin: admin, user: user}
     end
 
     test "sets password_reset_pending to true", %{admin: admin, user: user} do
-      assert user.info.password_reset_pending == false
+      assert user.password_reset_pending == false
 
       conn =
         build_conn()
@@ -2483,7 +2544,75 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
       ObanHelpers.perform_all()
 
-      assert User.get_by_id(user.id).info.password_reset_pending == true
+      assert User.get_by_id(user.id).password_reset_pending == true
+    end
+  end
+
+  describe "relays" do
+    setup %{conn: conn} do
+      admin = insert(:user, is_admin: true)
+
+      %{conn: assign(conn, :user, admin), admin: admin}
+    end
+
+    test "POST /relay", %{admin: admin} do
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> post("/api/pleroma/admin/relay", %{
+          relay_url: "http://mastodon.example.org/users/admin"
+        })
+
+      assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
+
+      log_entry = Repo.one(ModerationLog)
+
+      assert ModerationLog.get_log_entry_message(log_entry) ==
+               "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
+    end
+
+    test "GET /relay", %{admin: admin} do
+      Pleroma.Web.ActivityPub.Relay.get_actor()
+      |> Ecto.Changeset.change(
+        following: [
+          "http://test-app.com/user/test1",
+          "http://test-app.com/user/test1",
+          "http://test-app-42.com/user/test1"
+        ]
+      )
+      |> Pleroma.User.update_and_set_cache()
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> get("/api/pleroma/admin/relay")
+
+      assert json_response(conn, 200)["relays"] -- ["test-app.com", "test-app-42.com"] == []
+    end
+
+    test "DELETE /relay", %{admin: admin} do
+      build_conn()
+      |> assign(:user, admin)
+      |> post("/api/pleroma/admin/relay", %{
+        relay_url: "http://mastodon.example.org/users/admin"
+      })
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> delete("/api/pleroma/admin/relay", %{
+          relay_url: "http://mastodon.example.org/users/admin"
+        })
+
+      assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
+
+      [log_entry_one, log_entry_two] = Repo.all(ModerationLog)
+
+      assert ModerationLog.get_log_entry_message(log_entry_one) ==
+               "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
+
+      assert ModerationLog.get_log_entry_message(log_entry_two) ==
+               "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
     end
   end
 end
index 9df4cd539d0aae12b7c6b26202a8422651f114d0..082e691c4b54195e59e0489bcdfa404bfaa0a977 100644 (file)
@@ -47,9 +47,9 @@ defmodule Pleroma.Web.AdminAPI.SearchTest do
     end
 
     test "it returns active/deactivated users" do
-      insert(:user, info: %{deactivated: true})
-      insert(:user, info: %{deactivated: true})
-      insert(:user, info: %{deactivated: false})
+      insert(:user, deactivated: true)
+      insert(:user, deactivated: true)
+      insert(:user, deactivated: false)
 
       {:ok, _results, active_count} =
         Search.user(%{
@@ -70,7 +70,7 @@ defmodule Pleroma.Web.AdminAPI.SearchTest do
     test "it returns specific user" do
       insert(:user)
       insert(:user)
-      user = insert(:user, nickname: "bob", local: true, info: %{deactivated: false})
+      user = insert(:user, nickname: "bob", local: true, deactivated: false)
 
       {:ok, _results, total_count} = Search.user(%{query: ""})
 
@@ -108,7 +108,7 @@ defmodule Pleroma.Web.AdminAPI.SearchTest do
     end
 
     test "it returns admin user" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       insert(:user)
       insert(:user)
 
@@ -119,7 +119,7 @@ defmodule Pleroma.Web.AdminAPI.SearchTest do
     end
 
     test "it returns moderator user" do
-      moderator = insert(:user, info: %{is_moderator: true})
+      moderator = insert(:user, is_moderator: true)
       insert(:user)
       insert(:user)
 
index 4757058576d2d5b5c54290d2f0ffbc5e9292c89e..ef4a806e4300fdc8300a82323fedecb9a47a2985 100644 (file)
@@ -49,6 +49,8 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do
     {:ok, report_activity} =
       CommonAPI.report(user, %{"account_id" => other_user.id, "status_ids" => [activity.id]})
 
+    other_user = Pleroma.User.get_by_id(other_user.id)
+
     expected = %{
       content: nil,
       actor:
index d69ed38a72f5fd67914714addc9ed915729ea6df..f88fa5ed6ccb5dac85d4dec0da2ff4cb13747e77 100644 (file)
@@ -101,7 +101,7 @@ defmodule Pleroma.Web.CommonAPITest do
 
     {:ok, activity} = CommonAPI.update(user)
     user = User.get_cached_by_ap_id(user.ap_id)
-    [firefox] = user.info.source_data["tag"]
+    [firefox] = user.source_data["tag"]
 
     assert firefox["name"] == ":firefox:"
 
@@ -292,7 +292,7 @@ defmodule Pleroma.Web.CommonAPITest do
       id = activity.id
       user = refresh_record(user)
 
-      assert %User{info: %{pinned_activities: [^id]}} = user
+      assert %User{pinned_activities: [^id]} = user
     end
 
     test "unlisted statuses can be pinned", %{user: user} do
@@ -326,7 +326,7 @@ defmodule Pleroma.Web.CommonAPITest do
 
       user = refresh_record(user)
 
-      assert %User{info: %{pinned_activities: []}} = user
+      assert %User{pinned_activities: []} = user
     end
 
     test "should unpin when deleting a status", %{user: user, activity: activity} do
@@ -338,7 +338,7 @@ defmodule Pleroma.Web.CommonAPITest do
 
       user = refresh_record(user)
 
-      assert %User{info: %{pinned_activities: []}} = user
+      assert %User{pinned_activities: []} = user
     end
   end
 
@@ -482,7 +482,7 @@ defmodule Pleroma.Web.CommonAPITest do
 
   describe "accept_follow_request/2" do
     test "after acceptance, it sets all existing pending follow request states to 'accept'" do
-      user = insert(:user, info: %{locked: true})
+      user = insert(:user, locked: true)
       follower = insert(:user)
       follower_two = insert(:user)
 
@@ -502,7 +502,7 @@ defmodule Pleroma.Web.CommonAPITest do
     end
 
     test "after rejection, it sets all existing pending follow request states to 'reject'" do
-      user = insert(:user, info: %{locked: true})
+      user = insert(:user, locked: true)
       follower = insert(:user)
       follower_two = insert(:user)
 
index 43a715706fd06424575a795b7ace49c641cf768f..c224197c3ad3a910e1fd8ee208274086ee8983b9 100644 (file)
@@ -81,14 +81,16 @@ defmodule Pleroma.Web.FederatorTest do
         local: false,
         nickname: "nick1@domain.com",
         ap_id: "https://domain.com/users/nick1",
-        info: %{ap_enabled: true, source_data: %{"inbox" => inbox1}}
+        source_data: %{"inbox" => inbox1},
+        ap_enabled: true
       })
 
       insert(:user, %{
         local: false,
         nickname: "nick2@domain2.com",
         ap_id: "https://domain2.com/users/nick2",
-        info: %{ap_enabled: true, source_data: %{"inbox" => inbox2}}
+        source_data: %{"inbox" => inbox2},
+        ap_enabled: true
       })
 
       dt = NaiveDateTime.utc_now()
@@ -111,93 +113,6 @@ defmodule Pleroma.Web.FederatorTest do
                all_enqueued(worker: PublisherWorker)
              )
     end
-
-    test "it federates only to reachable instances via Websub" do
-      user = insert(:user)
-      websub_topic = Pleroma.Web.OStatus.feed_path(user)
-
-      sub1 =
-        insert(:websub_subscription, %{
-          topic: websub_topic,
-          state: "active",
-          callback: "http://pleroma.soykaf.com/cb"
-        })
-
-      sub2 =
-        insert(:websub_subscription, %{
-          topic: websub_topic,
-          state: "active",
-          callback: "https://pleroma2.soykaf.com/cb"
-        })
-
-      dt = NaiveDateTime.utc_now()
-      Instances.set_unreachable(sub2.callback, dt)
-
-      Instances.set_consistently_unreachable(sub1.callback)
-
-      {:ok, _activity} = CommonAPI.post(user, %{"status" => "HI"})
-
-      expected_callback = sub2.callback
-      expected_dt = NaiveDateTime.to_iso8601(dt)
-
-      ObanHelpers.perform(all_enqueued(worker: PublisherWorker))
-
-      assert ObanHelpers.member?(
-               %{
-                 "op" => "publish_one",
-                 "params" => %{
-                   "callback" => expected_callback,
-                   "unreachable_since" => expected_dt
-                 }
-               },
-               all_enqueued(worker: PublisherWorker)
-             )
-    end
-
-    test "it federates only to reachable instances via Salmon" do
-      user = insert(:user)
-
-      _remote_user1 =
-        insert(:user, %{
-          local: false,
-          nickname: "nick1@domain.com",
-          ap_id: "https://domain.com/users/nick1",
-          info: %{salmon: "https://domain.com/salmon"}
-        })
-
-      remote_user2 =
-        insert(:user, %{
-          local: false,
-          nickname: "nick2@domain2.com",
-          ap_id: "https://domain2.com/users/nick2",
-          info: %{salmon: "https://domain2.com/salmon"}
-        })
-
-      remote_user2_id = remote_user2.id
-
-      dt = NaiveDateTime.utc_now()
-      Instances.set_unreachable(remote_user2.ap_id, dt)
-
-      Instances.set_consistently_unreachable("domain.com")
-
-      {:ok, _activity} =
-        CommonAPI.post(user, %{"status" => "HI @nick1@domain.com, @nick2@domain2.com!"})
-
-      expected_dt = NaiveDateTime.to_iso8601(dt)
-
-      ObanHelpers.perform(all_enqueued(worker: PublisherWorker))
-
-      assert ObanHelpers.member?(
-               %{
-                 "op" => "publish_one",
-                 "params" => %{
-                   "recipient_id" => remote_user2_id,
-                   "unreachable_since" => expected_dt
-                 }
-               },
-               all_enqueued(worker: PublisherWorker)
-             )
-    end
   end
 
   describe "Receive an activity" do
index ab9dab352b7fffcec628f36b7d161db949480117..b5dbd4a25d3e201931644922eb42772227b20d55 100644 (file)
@@ -23,7 +23,7 @@ defmodule Pleroma.Web.MastodonAPI.MastoFEController do
     assert _result = json_response(conn, 200)
 
     user = User.get_cached_by_ap_id(user.ap_id)
-    assert user.info.settings == %{"programming" => "socks"}
+    assert user.settings == %{"programming" => "socks"}
   end
 
   describe "index/2 redirections" do
index 618031b403c7efb6610167098482d3887a02f079..519b56d6cb64255f00ba55e34baffe74ad783012 100644 (file)
@@ -153,7 +153,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
         |> json_response(200)
 
       assert response["pleroma"]["skip_thread_containment"] == true
-      assert refresh_record(user).info.skip_thread_containment
+      assert refresh_record(user).skip_thread_containment
     end
 
     test "updates the user's hide_follows status", %{conn: conn} do
index 745383757aa19e7ee8dd5ccc2f7e024f9f9186ba..00c83fd7b267495706a6ba1c486f1639848be0f9 100644 (file)
@@ -269,7 +269,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
 
     test "getting followers, hide_followers", %{conn: conn} do
       user = insert(:user)
-      other_user = insert(:user, %{info: %{hide_followers: true}})
+      other_user = insert(:user, hide_followers: true)
       {:ok, _user} = User.follow(user, other_user)
 
       conn =
@@ -281,7 +281,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
 
     test "getting followers, hide_followers, same user requesting", %{conn: conn} do
       user = insert(:user)
-      other_user = insert(:user, %{info: %{hide_followers: true}})
+      other_user = insert(:user, hide_followers: true)
       {:ok, _user} = User.follow(user, other_user)
 
       conn =
@@ -349,7 +349,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
     end
 
     test "getting following, hide_follows", %{conn: conn} do
-      user = insert(:user, %{info: %{hide_follows: true}})
+      user = insert(:user, hide_follows: true)
       other_user = insert(:user)
       {:ok, user} = User.follow(user, other_user)
 
@@ -361,7 +361,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
     end
 
     test "getting following, hide_follows, same user requesting", %{conn: conn} do
-      user = insert(:user, %{info: %{hide_follows: true}})
+      user = insert(:user, hide_follows: true)
       other_user = insert(:user)
       {:ok, user} = User.follow(user, other_user)
 
@@ -683,7 +683,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
       token_from_db = Repo.preload(token_from_db, :user)
       assert token_from_db.user
 
-      assert token_from_db.user.info.confirmation_pending
+      assert token_from_db.user.confirmation_pending
     end
 
     test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
@@ -727,7 +727,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
         token_from_db = Repo.preload(token_from_db, :user)
         assert token_from_db.user
 
-        assert token_from_db.user.info.confirmation_pending
+        assert token_from_db.user.confirmation_pending
       end
 
       conn =
@@ -812,7 +812,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
     end
 
     test "verify_credentials default scope unlisted", %{conn: conn} do
-      user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
+      user = insert(:user, default_scope: "unlisted")
 
       conn =
         conn
@@ -824,7 +824,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
     end
 
     test "locked accounts", %{conn: conn} do
-      user = insert(:user, %{info: %User.Info{default_scope: "private"}})
+      user = insert(:user, default_scope: "private")
 
       conn =
         conn
index a308a76201e216edb106cb92fb7b4ee971f41f61..542af494469575666e17718d1949bc76f43f799a 100644 (file)
@@ -17,7 +17,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
 
     {:ok, user_two} = User.follow(user_two, user_one)
 
-    assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 0
+    assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
 
     {:ok, direct} =
       CommonAPI.post(user_one, %{
@@ -25,7 +25,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
         "visibility" => "direct"
       })
 
-    assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 1
+    assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1
 
     {:ok, _follower_only} =
       CommonAPI.post(user_one, %{
@@ -54,9 +54,9 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
     assert user_two.id in account_ids
     assert user_three.id in account_ids
     assert is_binary(res_id)
-    assert unread == true
+    assert unread == false
     assert res_last_status["id"] == direct.id
-    assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1
+    assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0
   end
 
   test "updates the last_status on reply", %{conn: conn} do
@@ -95,19 +95,23 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
         "visibility" => "direct"
       })
 
+    assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0
+    assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1
+
     [%{"id" => direct_conversation_id, "unread" => true}] =
       conn
-      |> assign(:user, user_one)
+      |> assign(:user, user_two)
       |> get("/api/v1/conversations")
       |> json_response(200)
 
     %{"unread" => false} =
       conn
-      |> assign(:user, user_one)
+      |> assign(:user, user_two)
       |> post("/api/v1/conversations/#{direct_conversation_id}/read")
       |> json_response(200)
 
-    assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 0
+    assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0
+    assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
 
     # The conversation is marked as unread on reply
     {:ok, _} =
@@ -123,7 +127,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
       |> get("/api/v1/conversations")
       |> json_response(200)
 
-    assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1
+    assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1
+    assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
 
     # A reply doesn't increment the user's unread_conversation_count if the conversation is unread
     {:ok, _} =
@@ -133,7 +138,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
         "in_reply_to_status_id" => direct.id
       })
 
-    assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1
+    assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1
+    assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
   end
 
   test "(vanilla) Mastodon frontend behaviour", %{conn: conn} do
index 4bf292df57bc1c47948be9bbe4b5e04e6391064d..913f8c03896875497743dbf1994863fe117978b3 100644 (file)
@@ -12,7 +12,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do
 
   describe "locked accounts" do
     test "/api/v1/follow_requests works" do
-      user = insert(:user, %{info: %User.Info{locked: true}})
+      user = insert(:user, locked: true)
       other_user = insert(:user)
 
       {:ok, _activity} = ActivityPub.follow(other_user, user)
@@ -32,7 +32,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do
     end
 
     test "/api/v1/follow_requests/:id/authorize works" do
-      user = insert(:user, %{info: %User.Info{locked: true}})
+      user = insert(:user, locked: true)
       other_user = insert(:user)
 
       {:ok, _activity} = ActivityPub.follow(other_user, user)
@@ -57,7 +57,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do
     end
 
     test "/api/v1/follow_requests/:id/reject works" do
-      user = insert(:user, %{info: %User.Info{locked: true}})
+      user = insert(:user, locked: true)
       other_user = insert(:user)
 
       {:ok, _activity} = ActivityPub.follow(other_user, user)
index f8049f81f68e264c687cb181f961b9d42133c67e..e00de6b18871e8e8eb075243360984fbd27c525f 100644 (file)
@@ -41,20 +41,13 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do
     user = insert(:user, %{local: true})
 
     user2 = insert(:user, %{local: true})
-    {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
+    {:ok, _user2} = User.deactivate(user2, !user2.deactivated)
 
     insert(:user, %{local: false, nickname: "u@peer1.com"})
     insert(:user, %{local: false, nickname: "u@peer2.com"})
 
     {:ok, _} = Pleroma.Web.CommonAPI.post(user, %{"status" => "cofe"})
 
-    # Stats should count users with missing or nil `info.deactivated` value
-
-    {:ok, _user} =
-      user.id
-      |> User.get_cached_by_id()
-      |> User.update_info(&Ecto.Changeset.change(&1, %{deactivated: nil}))
-
     Pleroma.Stats.force_update()
 
     conn = get(conn, "/api/v1/instance")
diff --git a/test/web/mastodon_api/controllers/marker_controller_test.exs b/test/web/mastodon_api/controllers/marker_controller_test.exs
new file mode 100644 (file)
index 0000000..1fcad87
--- /dev/null
@@ -0,0 +1,124 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do
+  use Pleroma.Web.ConnCase
+
+  import Pleroma.Factory
+
+  describe "GET /api/v1/markers" do
+    test "gets markers with correct scopes", %{conn: conn} do
+      user = insert(:user)
+      token = insert(:oauth_token, user: user, scopes: ["read:statuses"])
+
+      {:ok, %{"notifications" => marker}} =
+        Pleroma.Marker.upsert(
+          user,
+          %{"notifications" => %{"last_read_id" => "69420"}}
+        )
+
+      response =
+        conn
+        |> assign(:user, user)
+        |> assign(:token, token)
+        |> get("/api/v1/markers", %{timeline: ["notifications"]})
+        |> json_response(200)
+
+      assert response == %{
+               "notifications" => %{
+                 "last_read_id" => "69420",
+                 "updated_at" => NaiveDateTime.to_iso8601(marker.updated_at),
+                 "version" => 0
+               }
+             }
+    end
+
+    test "gets markers with missed scopes", %{conn: conn} do
+      user = insert(:user)
+      token = insert(:oauth_token, user: user, scopes: [])
+
+      Pleroma.Marker.upsert(user, %{"notifications" => %{"last_read_id" => "69420"}})
+
+      response =
+        conn
+        |> assign(:user, user)
+        |> assign(:token, token)
+        |> get("/api/v1/markers", %{timeline: ["notifications"]})
+        |> json_response(403)
+
+      assert response == %{"error" => "Insufficient permissions: read:statuses."}
+    end
+  end
+
+  describe "POST /api/v1/markers" do
+    test "creates a marker with correct scopes", %{conn: conn} do
+      user = insert(:user)
+      token = insert(:oauth_token, user: user, scopes: ["write:statuses"])
+
+      response =
+        conn
+        |> assign(:user, user)
+        |> assign(:token, token)
+        |> post("/api/v1/markers", %{
+          home: %{last_read_id: "777"},
+          notifications: %{"last_read_id" => "69420"}
+        })
+        |> json_response(200)
+
+      assert %{
+               "notifications" => %{
+                 "last_read_id" => "69420",
+                 "updated_at" => _,
+                 "version" => 0
+               }
+             } = response
+    end
+
+    test "updates exist marker", %{conn: conn} do
+      user = insert(:user)
+      token = insert(:oauth_token, user: user, scopes: ["write:statuses"])
+
+      {:ok, %{"notifications" => marker}} =
+        Pleroma.Marker.upsert(
+          user,
+          %{"notifications" => %{"last_read_id" => "69477"}}
+        )
+
+      response =
+        conn
+        |> assign(:user, user)
+        |> assign(:token, token)
+        |> post("/api/v1/markers", %{
+          home: %{last_read_id: "777"},
+          notifications: %{"last_read_id" => "69888"}
+        })
+        |> json_response(200)
+
+      assert response == %{
+               "notifications" => %{
+                 "last_read_id" => "69888",
+                 "updated_at" => NaiveDateTime.to_iso8601(marker.updated_at),
+                 "version" => 0
+               }
+             }
+    end
+
+    test "creates a marker with missed scopes", %{conn: conn} do
+      user = insert(:user)
+      token = insert(:oauth_token, user: user, scopes: [])
+
+      response =
+        conn
+        |> assign(:user, user)
+        |> assign(:token, token)
+        |> post("/api/v1/markers", %{
+          home: %{last_read_id: "777"},
+          notifications: %{"last_read_id" => "69420"}
+        })
+        |> json_response(403)
+
+      assert response == %{"error" => "Insufficient permissions: write:statuses."}
+    end
+  end
+end
index ee413eef7c8219a4f132f7df794db8397f185a29..7953fad62c197ada25def63fd8821988c7a19184 100644 (file)
@@ -204,17 +204,17 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
       conn =
         conn
         |> assign(:user, user)
-        |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
+        |> get("/api/v1/search", %{"q" => "mike@osada.macgirvin.com", "resolve" => "true"})
 
       assert results = json_response(conn, 200)
       [account] = results["accounts"]
-      assert account["acct"] == "shp@social.heldscal.la"
+      assert account["acct"] == "mike@osada.macgirvin.com"
     end
 
     test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do
       conn =
         conn
-        |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "false"})
+        |> get("/api/v1/search", %{"q" => "mike@osada.macgirvin.com", "resolve" => "false"})
 
       assert results = json_response(conn, 200)
       assert [] == results["accounts"]
index 2de2725e014023ae17e1f15068f2b9afc6c93931..a96fd860b50fe2edb08996e0cf7af3d5bc93137b 100644 (file)
@@ -12,12 +12,16 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
   alias Pleroma.Object
   alias Pleroma.Repo
   alias Pleroma.ScheduledActivity
+  alias Pleroma.Tests.ObanHelpers
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.CommonAPI
 
   import Pleroma.Factory
 
+  clear_config([:instance, :federating])
+  clear_config([:instance, :allow_relay])
+
   describe "posting statuses" do
     setup do
       user = insert(:user)
@@ -29,6 +33,34 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
       [conn: conn]
     end
 
+    test "posting a status does not increment reblog_count when relaying", %{conn: conn} do
+      Pleroma.Config.put([:instance, :federating], true)
+      Pleroma.Config.get([:instance, :allow_relay], true)
+      user = insert(:user)
+
+      response =
+        conn
+        |> assign(:user, user)
+        |> post("api/v1/statuses", %{
+          "content_type" => "text/plain",
+          "source" => "Pleroma FE",
+          "status" => "Hello world",
+          "visibility" => "public"
+        })
+        |> json_response(200)
+
+      assert response["reblogs_count"] == 0
+      ObanHelpers.perform_all()
+
+      response =
+        conn
+        |> assign(:user, user)
+        |> get("api/v1/statuses/#{response["id"]}", %{})
+        |> json_response(200)
+
+      assert response["reblogs_count"] == 0
+    end
+
     test "posting a status", %{conn: conn} do
       idempotency_key = "Pikachu rocks!"
 
@@ -526,8 +558,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
     test "when you're an admin or moderator", %{conn: conn} do
       activity1 = insert(:note_activity)
       activity2 = insert(:note_activity)
-      admin = insert(:user, info: %{is_admin: true})
-      moderator = insert(:user, info: %{is_moderator: true})
+      admin = insert(:user, is_admin: true)
+      moderator = insert(:user, is_moderator: true)
 
       res_conn =
         conn
index fc45c25de98c893a7f31ca67282463348064c43e..61b6cea7547c49ec2d90a115b8cf2e4abd248e3f 100644 (file)
@@ -11,7 +11,6 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
   alias Pleroma.Config
   alias Pleroma.User
   alias Pleroma.Web.CommonAPI
-  alias Pleroma.Web.OStatus
 
   clear_config([:instance, :public])
 
@@ -75,8 +74,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
 
       {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
 
-      {:ok, [_activity]} =
-        OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
+      _activity = insert(:note_activity, local: false)
 
       conn = get(conn, "/api/v1/timelines/public", %{"local" => "False"})
 
@@ -271,9 +269,6 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
 
       {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
 
-      {:ok, [_activity]} =
-        OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
-
       nconn = get(conn, "/api/v1/timelines/tag/2hu")
 
       assert [%{"id" => id}] = json_response(nconn, :ok)
index 7fcb2bd55ddf05a7dd86bf7edc1f31de874cc90c..561ef05aa4dab69ab0a105a8c3b4401e8a2dd458 100644 (file)
@@ -14,11 +14,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do
   import Pleroma.Factory
 
   describe "follow/3" do
-    test "returns error when user deactivated" do
+    test "returns error when followed user is deactivated" do
       follower = insert(:user)
-      user = insert(:user, local: true, info: %{deactivated: true})
+      user = insert(:user, local: true, deactivated: true)
       {:error, error} = MastodonAPI.follow(follower, user)
-      assert error == "Could not follow user: You are deactivated."
+      assert error == "Could not follow user: #{user.nickname} is deactivated."
     end
 
     test "following for user" do
index b7a4938a65996df0ea312bdd9381817aed0dc7b2..af88841ed234c5f8ba87a49312a79abe302247fe 100644 (file)
@@ -26,12 +26,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
 
     user =
       insert(:user, %{
-        info: %{
-          note_count: 5,
-          follower_count: 3,
-          source_data: source_data,
-          background: background_image
-        },
+        follower_count: 3,
+        note_count: 5,
+        source_data: source_data,
+        background: background_image,
         nickname: "shp@shitposter.club",
         name: ":karjalanpiirakka: shp",
         bio: "<script src=\"invalid-html\"></script><span>valid html</span>",
@@ -101,7 +99,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
       "non_followers" => true
     }
 
-    privacy = user.info.default_scope
+    privacy = user.default_scope
 
     assert %{
              pleroma: %{notification_settings: ^notification_settings},
@@ -112,7 +110,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
   test "Represent a Service(bot) account" do
     user =
       insert(:user, %{
-        info: %{note_count: 5, follower_count: 3, source_data: %{"type" => "Service"}},
+        follower_count: 3,
+        note_count: 5,
+        source_data: %{"type" => "Service"},
         nickname: "shp@shitposter.club",
         inserted_at: ~N[2017-08-15 15:47:06.597036]
       })
@@ -164,8 +164,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
   end
 
   test "Represent a deactivated user for an admin" do
-    admin = insert(:user, %{info: %{is_admin: true}})
-    deactivated_user = insert(:user, %{info: %{deactivated: true}})
+    admin = insert(:user, is_admin: true)
+    deactivated_user = insert(:user, deactivated: true)
     represented = AccountView.render("show.json", %{user: deactivated_user, for: admin})
     assert represented[:pleroma][:deactivated] == true
   end
@@ -253,7 +253,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
 
     test "represent a relationship for the user with a pending follow request" do
       user = insert(:user)
-      other_user = insert(:user, %{info: %User.Info{locked: true}})
+      other_user = insert(:user, locked: true)
 
       {:ok, user, other_user, _} = CommonAPI.follow(user, other_user)
       user = User.get_cached_by_id(user.id)
@@ -282,7 +282,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
   test "represent an embedded relationship" do
     user =
       insert(:user, %{
-        info: %{note_count: 5, follower_count: 0, source_data: %{"type" => "Service"}},
+        follower_count: 0,
+        note_count: 5,
+        source_data: %{"type" => "Service"},
         nickname: "shp@shitposter.club",
         inserted_at: ~N[2017-08-15 15:47:06.597036]
       })
@@ -352,7 +354,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
   end
 
   test "returns the settings store if the requesting user is the represented user and it's requested specifically" do
-    user = insert(:user, %{info: %User.Info{pleroma_settings_store: %{fe: "test"}}})
+    user = insert(:user, pleroma_settings_store: %{fe: "test"})
 
     result =
       AccountView.render("show.json", %{user: user, for: user, with_pleroma_settings: true})
@@ -374,14 +376,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
 
   describe "hiding follows/following" do
     test "shows when follows/followers stats are hidden and sets follow/follower count to 0" do
-      info = %{
-        hide_followers: true,
-        hide_followers_count: true,
-        hide_follows: true,
-        hide_follows_count: true
-      }
-
-      user = insert(:user, info: info)
+      user =
+        insert(:user, %{
+          hide_followers: true,
+          hide_followers_count: true,
+          hide_follows: true,
+          hide_follows_count: true
+        })
 
       other_user = insert(:user)
       {:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user)
@@ -395,7 +396,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
     end
 
     test "shows when follows/followers are hidden" do
-      user = insert(:user, info: %{hide_followers: true, hide_follows: true})
+      user = insert(:user, hide_followers: true, hide_follows: true)
       other_user = insert(:user)
       {:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user)
       {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
@@ -408,7 +409,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
     end
 
     test "shows actual follower/following count to the account owner" do
-      user = insert(:user, info: %{hide_followers: true, hide_follows: true})
+      user = insert(:user, hide_followers: true, hide_follows: true)
       other_user = insert(:user)
       {:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user)
       {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
@@ -424,8 +425,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
       other_user = insert(:user)
 
       {:ok, _activity} =
-        CommonAPI.post(user, %{
-          "status" => "Hey @#{other_user.nickname}.",
+        CommonAPI.post(other_user, %{
+          "status" => "Hey @#{user.nickname}.",
           "visibility" => "direct"
         })
 
@@ -456,7 +457,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
     end
 
     test "shows non-zero when follow requests are pending" do
-      user = insert(:user, %{info: %{locked: true}})
+      user = insert(:user, locked: true)
 
       assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user})
 
@@ -468,7 +469,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
     end
 
     test "decreases when accepting a follow request" do
-      user = insert(:user, %{info: %{locked: true}})
+      user = insert(:user, locked: true)
 
       assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user})
 
@@ -485,7 +486,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
     end
 
     test "decreases when rejecting a follow request" do
-      user = insert(:user, %{info: %{locked: true}})
+      user = insert(:user, locked: true)
 
       assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user})
 
@@ -502,14 +503,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
     end
 
     test "shows non-zero when historical unapproved requests are present" do
-      user = insert(:user, %{info: %{locked: true}})
+      user = insert(:user, locked: true)
 
       assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user})
 
       other_user = insert(:user)
       {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
 
-      {:ok, user} = User.update_info(user, &User.Info.user_upgrade(&1, %{locked: false}))
+      {:ok, user} = User.update_and_set_cache(user, %{locked: false})
 
       assert %{locked: false, follow_requests_count: 1} =
                AccountView.render("show.json", %{user: user, for: user})
diff --git a/test/web/mastodon_api/views/marker_view_test.exs b/test/web/mastodon_api/views/marker_view_test.exs
new file mode 100644 (file)
index 0000000..8a5c89d
--- /dev/null
@@ -0,0 +1,27 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.MarkerViewTest do
+  use Pleroma.DataCase
+  alias Pleroma.Web.MastodonAPI.MarkerView
+  import Pleroma.Factory
+
+  test "returns markers" do
+    marker1 = insert(:marker, timeline: "notifications", last_read_id: "17")
+    marker2 = insert(:marker, timeline: "home", last_read_id: "42")
+
+    assert MarkerView.render("markers.json", %{markers: [marker1, marker2]}) == %{
+             "home" => %{
+               last_read_id: "42",
+               updated_at: NaiveDateTime.to_iso8601(marker2.updated_at),
+               version: 0
+             },
+             "notifications" => %{
+               last_read_id: "17",
+               updated_at: NaiveDateTime.to_iso8601(marker1.updated_at),
+               version: 0
+             }
+           }
+  end
+end
index 1d5a6e956db95d7be1edbc792c032f2e59145d92..c200ad8feb6d353cb1499da5e5060581c6f49a5d 100644 (file)
@@ -14,7 +14,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
   alias Pleroma.Web.CommonAPI.Utils
   alias Pleroma.Web.MastodonAPI.AccountView
   alias Pleroma.Web.MastodonAPI.StatusView
-  alias Pleroma.Web.OStatus
   import Pleroma.Factory
   import Tesla.Mock
 
@@ -230,17 +229,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
   end
 
   test "contains mentions" do
-    incoming = File.read!("test/fixtures/incoming_reply_mastodon.xml")
-    # a user with this ap id might be in the cache.
-    recipient = "https://pleroma.soykaf.com/users/lain"
-    user = insert(:user, %{ap_id: recipient})
+    user = insert(:user)
+    mentioned = insert(:user)
 
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
+    {:ok, activity} = CommonAPI.post(user, %{"status" => "hi @#{mentioned.nickname}"})
 
     status = StatusView.render("show.json", %{activity: activity})
 
     assert status.mentions ==
-             Enum.map([user], fn u -> AccountView.render("mention.json", %{user: u}) end)
+             Enum.map([mentioned], fn u -> AccountView.render("mention.json", %{user: u}) end)
   end
 
   test "create mentions from the 'to' field" do
index e15a0bfff0c957f8203558b7a29d7b835a07c8e5..a3281b25b7235036797bffd055ebce706d41be2e 100644 (file)
@@ -24,8 +24,8 @@ defmodule Pleroma.Web.NodeInfoTest do
   end
 
   test "nodeinfo shows staff accounts", %{conn: conn} do
-    moderator = insert(:user, %{local: true, info: %{is_moderator: true}})
-    admin = insert(:user, %{local: true, info: %{is_admin: true}})
+    moderator = insert(:user, local: true, is_moderator: true)
+    admin = insert(:user, local: true, is_admin: true)
 
     conn =
       conn
index 41aaf6189d71d0904a32c459bcd8b2a838d53386..ad8d7908363f99f18fd213477f5b4c408a634448 100644 (file)
@@ -780,8 +780,8 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
 
       {:ok, user} =
         insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password))
-        |> User.change_info(&User.Info.confirmation_changeset(&1, need_confirmation: true))
-        |> Repo.update()
+        |> User.confirmation_changeset(need_confirmation: true)
+        |> User.update_and_set_cache()
 
       refute Pleroma.User.auth_active?(user)
 
@@ -808,7 +808,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
       user =
         insert(:user,
           password_hash: Comeonin.Pbkdf2.hashpwsalt(password),
-          info: %{deactivated: true}
+          deactivated: true
         )
 
       app = insert(:oauth_app)
@@ -834,7 +834,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
       user =
         insert(:user,
           password_hash: Comeonin.Pbkdf2.hashpwsalt(password),
-          info: %{password_reset_pending: true}
+          password_reset_pending: true
         )
 
       app = insert(:oauth_app, scopes: ["read", "write"])
diff --git a/test/web/ostatus/activity_representer_test.exs b/test/web/ostatus/activity_representer_test.exs
deleted file mode 100644 (file)
index a8d5008..0000000
+++ /dev/null
@@ -1,300 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
-  use Pleroma.DataCase
-
-  alias Pleroma.Activity
-  alias Pleroma.Object
-  alias Pleroma.User
-  alias Pleroma.Web.ActivityPub.ActivityPub
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.OStatus.ActivityRepresenter
-
-  import Pleroma.Factory
-  import Tesla.Mock
-
-  setup do
-    mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
-    :ok
-  end
-
-  test "an external note activity" do
-    incoming = File.read!("test/fixtures/mastodon-note-cw.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-
-    user = User.get_cached_by_ap_id(activity.data["actor"])
-
-    tuple = ActivityRepresenter.to_simple_form(activity, user)
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary()
-
-    assert String.contains?(
-             res,
-             ~s{<link type="text/html" href="https://mastodon.social/users/lambadalambda/updates/2314748" rel="alternate"/>}
-           )
-  end
-
-  test "a note activity" do
-    note_activity = insert(:note_activity)
-    object_data = Object.normalize(note_activity).data
-
-    user = User.get_cached_by_ap_id(note_activity.data["actor"])
-
-    expected = """
-    <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
-    <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
-    <id>#{object_data["id"]}</id>
-    <title>New note by #{user.nickname}</title>
-    <content type="html">#{object_data["content"]}</content>
-    <published>#{object_data["published"]}</published>
-    <updated>#{object_data["published"]}</updated>
-    <ostatus:conversation ref="#{note_activity.data["context"]}">#{note_activity.data["context"]}</ostatus:conversation>
-    <link ref="#{note_activity.data["context"]}" rel="ostatus:conversation" />
-    <summary>#{object_data["summary"]}</summary>
-    <link type="application/atom+xml" href="#{object_data["id"]}" rel="self" />
-    <link type="text/html" href="#{object_data["id"]}" rel="alternate" />
-    <category term="2hu"/>
-    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
-    <link name="2hu" rel="emoji" href="corndog.png" />
-    """
-
-    tuple = ActivityRepresenter.to_simple_form(note_activity, user)
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary()
-
-    assert clean(res) == clean(expected)
-  end
-
-  test "a reply note" do
-    user = insert(:user)
-    note_object = insert(:note)
-    _note = insert(:note_activity, %{note: note_object})
-    object = insert(:note, %{data: %{"inReplyTo" => note_object.data["id"]}})
-    answer = insert(:note_activity, %{note: object})
-
-    Repo.update!(
-      Object.change(note_object, %{data: Map.put(note_object.data, "external_url", "someurl")})
-    )
-
-    expected = """
-    <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
-    <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
-    <id>#{object.data["id"]}</id>
-    <title>New note by #{user.nickname}</title>
-    <content type="html">#{object.data["content"]}</content>
-    <published>#{object.data["published"]}</published>
-    <updated>#{object.data["published"]}</updated>
-    <ostatus:conversation ref="#{answer.data["context"]}">#{answer.data["context"]}</ostatus:conversation>
-    <link ref="#{answer.data["context"]}" rel="ostatus:conversation" />
-    <summary>2hu</summary>
-    <link type="application/atom+xml" href="#{object.data["id"]}" rel="self" />
-    <link type="text/html" href="#{object.data["id"]}" rel="alternate" />
-    <category term="2hu"/>
-    <thr:in-reply-to ref="#{note_object.data["id"]}" href="someurl" />
-    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
-    <link name="2hu" rel="emoji" href="corndog.png" />
-    """
-
-    tuple = ActivityRepresenter.to_simple_form(answer, user)
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary()
-
-    assert clean(res) == clean(expected)
-  end
-
-  test "an announce activity" do
-    note = insert(:note_activity)
-    user = insert(:user)
-    object = Object.normalize(note)
-
-    {:ok, announce, _object} = ActivityPub.announce(user, object)
-
-    announce = Activity.get_by_id(announce.id)
-
-    note_user = User.get_cached_by_ap_id(note.data["actor"])
-    note = Activity.get_by_id(note.id)
-
-    note_xml =
-      ActivityRepresenter.to_simple_form(note, note_user, true)
-      |> :xmerl.export_simple_content(:xmerl_xml)
-      |> to_string
-
-    expected = """
-    <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
-    <activity:verb>http://activitystrea.ms/schema/1.0/share</activity:verb>
-    <id>#{announce.data["id"]}</id>
-    <title>#{user.nickname} repeated a notice</title>
-    <content type="html">RT #{object.data["content"]}</content>
-    <published>#{announce.data["published"]}</published>
-    <updated>#{announce.data["published"]}</updated>
-    <ostatus:conversation ref="#{announce.data["context"]}">#{announce.data["context"]}</ostatus:conversation>
-    <link ref="#{announce.data["context"]}" rel="ostatus:conversation" />
-    <link rel="self" type="application/atom+xml" href="#{announce.data["id"]}"/>
-    <activity:object>
-      #{note_xml}
-    </activity:object>
-    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="#{
-      note.data["actor"]
-    }"/>
-    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
-    """
-
-    announce_xml =
-      ActivityRepresenter.to_simple_form(announce, user)
-      |> :xmerl.export_simple_content(:xmerl_xml)
-      |> to_string
-
-    assert clean(expected) == clean(announce_xml)
-  end
-
-  test "a like activity" do
-    note = insert(:note)
-    user = insert(:user)
-    {:ok, like, _note} = ActivityPub.like(user, note)
-
-    tuple = ActivityRepresenter.to_simple_form(like, user)
-    refute is_nil(tuple)
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary()
-
-    expected = """
-    <activity:verb>http://activitystrea.ms/schema/1.0/favorite</activity:verb>
-    <id>#{like.data["id"]}</id>
-    <title>New favorite by #{user.nickname}</title>
-    <content type="html">#{user.nickname} favorited something</content>
-    <published>#{like.data["published"]}</published>
-    <updated>#{like.data["published"]}</updated>
-    <activity:object>
-      <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
-      <id>#{note.data["id"]}</id>
-    </activity:object>
-    <ostatus:conversation ref="#{like.data["context"]}">#{like.data["context"]}</ostatus:conversation>
-    <link ref="#{like.data["context"]}" rel="ostatus:conversation" />
-    <link rel="self" type="application/atom+xml" href="#{like.data["id"]}"/>
-    <thr:in-reply-to ref="#{note.data["id"]}" />
-    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="#{
-      note.data["actor"]
-    }"/>
-    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
-    """
-
-    assert clean(res) == clean(expected)
-  end
-
-  test "a follow activity" do
-    follower = insert(:user)
-    followed = insert(:user)
-
-    {:ok, activity} =
-      ActivityPub.insert(%{
-        "type" => "Follow",
-        "actor" => follower.ap_id,
-        "object" => followed.ap_id,
-        "to" => [followed.ap_id]
-      })
-
-    tuple = ActivityRepresenter.to_simple_form(activity, follower)
-
-    refute is_nil(tuple)
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary()
-
-    expected = """
-    <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
-    <activity:verb>http://activitystrea.ms/schema/1.0/follow</activity:verb>
-    <id>#{activity.data["id"]}</id>
-    <title>#{follower.nickname} started following #{activity.data["object"]}</title>
-    <content type="html"> #{follower.nickname} started following #{activity.data["object"]}</content>
-    <published>#{activity.data["published"]}</published>
-    <updated>#{activity.data["published"]}</updated>
-    <activity:object>
-      <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
-      <id>#{activity.data["object"]}</id>
-      <uri>#{activity.data["object"]}</uri>
-    </activity:object>
-    <link rel="self" type="application/atom+xml" href="#{activity.data["id"]}"/>
-    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="#{
-      activity.data["object"]
-    }"/>
-    """
-
-    assert clean(res) == clean(expected)
-  end
-
-  test "an unfollow activity" do
-    follower = insert(:user)
-    followed = insert(:user)
-    {:ok, _activity} = ActivityPub.follow(follower, followed)
-    {:ok, activity} = ActivityPub.unfollow(follower, followed)
-
-    tuple = ActivityRepresenter.to_simple_form(activity, follower)
-
-    refute is_nil(tuple)
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary()
-
-    expected = """
-    <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
-    <activity:verb>http://activitystrea.ms/schema/1.0/unfollow</activity:verb>
-    <id>#{activity.data["id"]}</id>
-    <title>#{follower.nickname} stopped following #{followed.ap_id}</title>
-    <content type="html"> #{follower.nickname} stopped following #{followed.ap_id}</content>
-    <published>#{activity.data["published"]}</published>
-    <updated>#{activity.data["published"]}</updated>
-    <activity:object>
-      <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
-      <id>#{followed.ap_id}</id>
-      <uri>#{followed.ap_id}</uri>
-    </activity:object>
-    <link rel="self" type="application/atom+xml" href="#{activity.data["id"]}"/>
-    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="#{
-      followed.ap_id
-    }"/>
-    """
-
-    assert clean(res) == clean(expected)
-  end
-
-  test "a delete" do
-    user = insert(:user)
-
-    activity = %Activity{
-      data: %{
-        "id" => "ap_id",
-        "type" => "Delete",
-        "actor" => user.ap_id,
-        "object" => "some_id",
-        "published" => "2017-06-18T12:00:18+00:00"
-      }
-    }
-
-    tuple = ActivityRepresenter.to_simple_form(activity, nil)
-
-    refute is_nil(tuple)
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary()
-
-    expected = """
-    <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
-    <activity:verb>http://activitystrea.ms/schema/1.0/delete</activity:verb>
-    <id>#{activity.data["object"]}</id>
-    <title>An object was deleted</title>
-    <content type="html">An object was deleted</content>
-    <published>#{activity.data["published"]}</published>
-    <updated>#{activity.data["published"]}</updated>
-    """
-
-    assert clean(res) == clean(expected)
-  end
-
-  test "an unknown activity" do
-    tuple = ActivityRepresenter.to_simple_form(%Activity{}, nil)
-    assert is_nil(tuple)
-  end
-
-  defp clean(string) do
-    String.replace(string, ~r/\s/, "")
-  end
-end
diff --git a/test/web/ostatus/feed_representer_test.exs b/test/web/ostatus/feed_representer_test.exs
deleted file mode 100644 (file)
index d1cadf1..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.FeedRepresenterTest do
-  use Pleroma.DataCase
-  import Pleroma.Factory
-  alias Pleroma.User
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.OStatus.ActivityRepresenter
-  alias Pleroma.Web.OStatus.FeedRepresenter
-  alias Pleroma.Web.OStatus.UserRepresenter
-
-  test "returns a feed of the last 20 items of the user" do
-    note_activity = insert(:note_activity)
-    user = User.get_cached_by_ap_id(note_activity.data["actor"])
-
-    tuple = FeedRepresenter.to_simple_form(user, [note_activity], [user])
-
-    most_recent_update =
-      note_activity.updated_at
-      |> NaiveDateTime.to_iso8601()
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> to_string
-
-    user_xml =
-      UserRepresenter.to_simple_form(user)
-      |> :xmerl.export_simple_content(:xmerl_xml)
-
-    entry_xml =
-      ActivityRepresenter.to_simple_form(note_activity, user)
-      |> :xmerl.export_simple_content(:xmerl_xml)
-
-    expected = """
-    <feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:ostatus="http://ostatus.org/schema/1.0">
-      <id>#{OStatus.feed_path(user)}</id>
-      <title>#{user.nickname}'s timeline</title>
-      <updated>#{most_recent_update}</updated>
-      <logo>#{User.avatar_url(user)}</logo>
-      <link rel="hub" href="#{OStatus.pubsub_path(user)}" />
-      <link rel="salmon" href="#{OStatus.salmon_path(user)}" />
-      <link rel="self" href="#{OStatus.feed_path(user)}" type="application/atom+xml" />
-      <author>
-        #{user_xml}
-      </author>
-      <link rel="next" href="#{OStatus.feed_path(user)}?max_id=#{note_activity.id}" type="application/atom+xml" />
-      <entry>
-        #{entry_xml}
-      </entry>
-    </feed>
-    """
-
-    assert clean(res) == clean(expected)
-  end
-
-  defp clean(string) do
-    String.replace(string, ~r/\s/, "")
-  end
-end
diff --git a/test/web/ostatus/incoming_documents/delete_handling_test.exs b/test/web/ostatus/incoming_documents/delete_handling_test.exs
deleted file mode 100644 (file)
index cd0447a..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.DeleteHandlingTest do
-  use Pleroma.DataCase
-
-  import Pleroma.Factory
-  import Tesla.Mock
-
-  alias Pleroma.Activity
-  alias Pleroma.Object
-  alias Pleroma.Web.OStatus
-
-  setup do
-    mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
-    :ok
-  end
-
-  describe "deletions" do
-    test "it removes the mentioned activity" do
-      note = insert(:note_activity)
-      second_note = insert(:note_activity)
-      object = Object.normalize(note)
-      second_object = Object.normalize(second_note)
-      user = insert(:user)
-
-      {:ok, like, _object} = Pleroma.Web.ActivityPub.ActivityPub.like(user, object)
-
-      incoming =
-        File.read!("test/fixtures/delete.xml")
-        |> String.replace(
-          "tag:mastodon.sdf.org,2017-06-10:objectId=310513:objectType=Status",
-          object.data["id"]
-        )
-
-      {:ok, [delete]} = OStatus.handle_incoming(incoming)
-
-      refute Activity.get_by_id(note.id)
-      refute Activity.get_by_id(like.id)
-      assert Object.get_by_ap_id(object.data["id"]).data["type"] == "Tombstone"
-      assert Activity.get_by_id(second_note.id)
-      assert Object.get_by_ap_id(second_object.data["id"])
-
-      assert delete.data["type"] == "Delete"
-    end
-  end
-end
index b1af918d8792c8ce76076948c0e3ddac50e20287..37b7b62f5aa21b359861f6105aa2c1e36ce0a6c4 100644 (file)
@@ -5,13 +5,11 @@
 defmodule Pleroma.Web.OStatus.OStatusControllerTest do
   use Pleroma.Web.ConnCase
 
-  import ExUnit.CaptureLog
   import Pleroma.Factory
 
   alias Pleroma.Object
   alias Pleroma.User
   alias Pleroma.Web.CommonAPI
-  alias Pleroma.Web.OStatus.ActivityRepresenter
 
   setup_all do
     Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
@@ -22,78 +20,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
     Pleroma.Config.put([:instance, :federating], true)
   end
 
-  describe "salmon_incoming" do
-    test "decodes a salmon", %{conn: conn} do
-      user = insert(:user)
-      salmon = File.read!("test/fixtures/salmon.xml")
-
-      assert capture_log(fn ->
-               conn =
-                 conn
-                 |> put_req_header("content-type", "application/atom+xml")
-                 |> post("/users/#{user.nickname}/salmon", salmon)
-
-               assert response(conn, 200)
-             end) =~ "[error]"
-    end
-
-    test "decodes a salmon with a changed magic key", %{conn: conn} do
-      user = insert(:user)
-      salmon = File.read!("test/fixtures/salmon.xml")
-
-      assert capture_log(fn ->
-               conn =
-                 conn
-                 |> put_req_header("content-type", "application/atom+xml")
-                 |> post("/users/#{user.nickname}/salmon", salmon)
-
-               assert response(conn, 200)
-             end) =~ "[error]"
-
-      # Wrong key
-      info = %{
-        magic_key:
-          "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwrong1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAB"
-      }
-
-      # Set a wrong magic-key for a user so it has to refetch
-      "http://gs.example.org:4040/index.php/user/1"
-      |> User.get_cached_by_ap_id()
-      |> User.update_info(&User.Info.remote_user_creation(&1, info))
-
-      assert capture_log(fn ->
-               conn =
-                 build_conn()
-                 |> put_req_header("content-type", "application/atom+xml")
-                 |> post("/users/#{user.nickname}/salmon", salmon)
-
-               assert response(conn, 200)
-             end) =~ "[error]"
-    end
-  end
-
   describe "GET object/2" do
-    test "gets an object", %{conn: conn} do
-      note_activity = insert(:note_activity)
-      object = Object.normalize(note_activity)
-      user = User.get_cached_by_ap_id(note_activity.data["actor"])
-      [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
-      url = "/objects/#{uuid}"
-
-      conn =
-        conn
-        |> put_req_header("accept", "application/xml")
-        |> get(url)
-
-      expected =
-        ActivityRepresenter.to_simple_form(note_activity, user, true)
-        |> ActivityRepresenter.wrap_with_entry()
-        |> :xmerl.export_simple(:xmerl_xml)
-        |> to_string
-
-      assert response(conn, 200) == expected
-    end
-
     test "redirects to /notice/id for html format", %{conn: conn} do
       note_activity = insert(:note_activity)
       object = Object.normalize(note_activity)
@@ -143,16 +70,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
   end
 
   describe "GET activity/2" do
-    test "gets an activity in xml format", %{conn: conn} do
-      note_activity = insert(:note_activity)
-      [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
-
-      conn
-      |> put_req_header("accept", "application/xml")
-      |> get("/activities/#{uuid}")
-      |> response(200)
-    end
-
     test "redirects to /notice/id for html format", %{conn: conn} do
       note_activity = insert(:note_activity)
       [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
@@ -180,24 +97,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
       assert response(conn, 500) == ~S({"error":"Something went wrong"})
     end
 
-    test "404s on deleted objects", %{conn: conn} do
-      note_activity = insert(:note_activity)
-      object = Object.normalize(note_activity)
-      [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
-
-      conn
-      |> put_req_header("accept", "application/xml")
-      |> get("/objects/#{uuid}")
-      |> response(200)
-
-      Object.delete(object)
-
-      conn
-      |> put_req_header("accept", "application/xml")
-      |> get("/objects/#{uuid}")
-      |> response(404)
-    end
-
     test "404s on private activities", %{conn: conn} do
       note_activity = insert(:direct_note_activity)
       [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
diff --git a/test/web/ostatus/ostatus_test.exs b/test/web/ostatus/ostatus_test.exs
deleted file mode 100644 (file)
index 70a0e44..0000000
+++ /dev/null
@@ -1,645 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatusTest do
-  use Pleroma.DataCase
-  alias Pleroma.Activity
-  alias Pleroma.Instances
-  alias Pleroma.Object
-  alias Pleroma.Repo
-  alias Pleroma.User
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.XML
-
-  import ExUnit.CaptureLog
-  import Mock
-  import Pleroma.Factory
-
-  setup_all do
-    Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
-    :ok
-  end
-
-  test "don't insert create notes twice" do
-    incoming = File.read!("test/fixtures/incoming_note_activity.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    assert {:ok, [activity]} == OStatus.handle_incoming(incoming)
-  end
-
-  test "handle incoming note - GS, Salmon" do
-    incoming = File.read!("test/fixtures/incoming_note_activity.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity)
-
-    user = User.get_cached_by_ap_id(activity.data["actor"])
-    assert user.info.note_count == 1
-    assert activity.data["type"] == "Create"
-    assert object.data["type"] == "Note"
-
-    assert object.data["id"] == "tag:gs.example.org:4040,2017-04-23:noticeId=29:objectType=note"
-
-    assert activity.data["published"] == "2017-04-23T14:51:03+00:00"
-    assert object.data["published"] == "2017-04-23T14:51:03+00:00"
-
-    assert activity.data["context"] ==
-             "tag:gs.example.org:4040,2017-04-23:objectType=thread:nonce=f09e22f58abd5c7b"
-
-    assert "http://pleroma.example.org:4000/users/lain3" in activity.data["to"]
-    assert object.data["emoji"] == %{"marko" => "marko.png", "reimu" => "reimu.png"}
-    assert activity.local == false
-  end
-
-  test "handle incoming notes - GS, subscription" do
-    incoming = File.read!("test/fixtures/ostatus_incoming_post.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity)
-
-    assert activity.data["type"] == "Create"
-    assert object.data["type"] == "Note"
-    assert object.data["actor"] == "https://social.heldscal.la/user/23211"
-    assert object.data["content"] == "Will it blend?"
-    user = User.get_cached_by_ap_id(activity.data["actor"])
-    assert User.ap_followers(user) in activity.data["to"]
-    assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
-  end
-
-  test "handle incoming notes with attachments - GS, subscription" do
-    incoming = File.read!("test/fixtures/incoming_websub_gnusocial_attachments.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity)
-
-    assert activity.data["type"] == "Create"
-    assert object.data["type"] == "Note"
-    assert object.data["actor"] == "https://social.heldscal.la/user/23211"
-    assert object.data["attachment"] |> length == 2
-    assert object.data["external_url"] == "https://social.heldscal.la/notice/2020923"
-    assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
-  end
-
-  test "handle incoming notes with tags" do
-    incoming = File.read!("test/fixtures/ostatus_incoming_post_tag.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity)
-
-    assert object.data["tag"] == ["nsfw"]
-    assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
-  end
-
-  test "handle incoming notes - Mastodon, salmon, reply" do
-    # It uses the context of the replied to object
-    Repo.insert!(%Object{
-      data: %{
-        "id" => "https://pleroma.soykaf.com/objects/c237d966-ac75-4fe3-a87a-d89d71a3a7a4",
-        "context" => "2hu"
-      }
-    })
-
-    incoming = File.read!("test/fixtures/incoming_reply_mastodon.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity)
-
-    assert activity.data["type"] == "Create"
-    assert object.data["type"] == "Note"
-    assert object.data["actor"] == "https://mastodon.social/users/lambadalambda"
-    assert activity.data["context"] == "2hu"
-    assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
-  end
-
-  test "handle incoming notes - Mastodon, with CW" do
-    incoming = File.read!("test/fixtures/mastodon-note-cw.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity)
-
-    assert activity.data["type"] == "Create"
-    assert object.data["type"] == "Note"
-    assert object.data["actor"] == "https://mastodon.social/users/lambadalambda"
-    assert object.data["summary"] == "technologic"
-    assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
-  end
-
-  test "handle incoming unlisted messages, put public into cc" do
-    incoming = File.read!("test/fixtures/mastodon-note-unlisted.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity)
-
-    refute "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
-    assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["cc"]
-    refute "https://www.w3.org/ns/activitystreams#Public" in object.data["to"]
-    assert "https://www.w3.org/ns/activitystreams#Public" in object.data["cc"]
-  end
-
-  test "handle incoming retweets - Mastodon, with CW" do
-    incoming = File.read!("test/fixtures/cw_retweet.xml")
-    {:ok, [[_activity, retweeted_activity]]} = OStatus.handle_incoming(incoming)
-    retweeted_object = Object.normalize(retweeted_activity)
-
-    assert retweeted_object.data["summary"] == "Hey."
-  end
-
-  test "handle incoming notes - GS, subscription, reply" do
-    incoming = File.read!("test/fixtures/ostatus_incoming_reply.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity)
-
-    assert activity.data["type"] == "Create"
-    assert object.data["type"] == "Note"
-    assert object.data["actor"] == "https://social.heldscal.la/user/23211"
-
-    assert object.data["content"] ==
-             "@<a href=\"https://gs.archae.me/user/4687\" class=\"h-card u-url p-nickname mention\" title=\"shpbot\">shpbot</a> why not indeed."
-
-    assert object.data["inReplyTo"] ==
-             "tag:gs.archae.me,2017-04-30:noticeId=778260:objectType=note"
-
-    assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
-  end
-
-  test "handle incoming retweets - GS, subscription" do
-    incoming = File.read!("test/fixtures/share-gs.xml")
-    {:ok, [[activity, retweeted_activity]]} = OStatus.handle_incoming(incoming)
-
-    assert activity.data["type"] == "Announce"
-    assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
-    assert activity.data["object"] == retweeted_activity.data["object"]
-    assert "https://pleroma.soykaf.com/users/lain" in activity.data["to"]
-    refute activity.local
-
-    retweeted_activity = Activity.get_by_id(retweeted_activity.id)
-    retweeted_object = Object.normalize(retweeted_activity)
-    assert retweeted_activity.data["type"] == "Create"
-    assert retweeted_activity.data["actor"] == "https://pleroma.soykaf.com/users/lain"
-    refute retweeted_activity.local
-    assert retweeted_object.data["announcement_count"] == 1
-    assert String.contains?(retweeted_object.data["content"], "mastodon")
-    refute String.contains?(retweeted_object.data["content"], "Test account")
-  end
-
-  test "handle incoming retweets - GS, subscription - local message" do
-    incoming = File.read!("test/fixtures/share-gs-local.xml")
-    note_activity = insert(:note_activity)
-    object = Object.normalize(note_activity)
-    user = User.get_cached_by_ap_id(note_activity.data["actor"])
-
-    incoming =
-      incoming
-      |> String.replace("LOCAL_ID", object.data["id"])
-      |> String.replace("LOCAL_USER", user.ap_id)
-
-    {:ok, [[activity, retweeted_activity]]} = OStatus.handle_incoming(incoming)
-
-    assert activity.data["type"] == "Announce"
-    assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
-    assert activity.data["object"] == object.data["id"]
-    assert user.ap_id in activity.data["to"]
-    refute activity.local
-
-    retweeted_activity = Activity.get_by_id(retweeted_activity.id)
-    assert note_activity.id == retweeted_activity.id
-    assert retweeted_activity.data["type"] == "Create"
-    assert retweeted_activity.data["actor"] == user.ap_id
-    assert retweeted_activity.local
-    assert Object.normalize(retweeted_activity).data["announcement_count"] == 1
-  end
-
-  test "handle incoming retweets - Mastodon, salmon" do
-    incoming = File.read!("test/fixtures/share.xml")
-    {:ok, [[activity, retweeted_activity]]} = OStatus.handle_incoming(incoming)
-    retweeted_object = Object.normalize(retweeted_activity)
-
-    assert activity.data["type"] == "Announce"
-    assert activity.data["actor"] == "https://mastodon.social/users/lambadalambda"
-    assert activity.data["object"] == retweeted_activity.data["object"]
-
-    assert activity.data["id"] ==
-             "tag:mastodon.social,2017-05-03:objectId=4934452:objectType=Status"
-
-    refute activity.local
-    assert retweeted_activity.data["type"] == "Create"
-    assert retweeted_activity.data["actor"] == "https://pleroma.soykaf.com/users/lain"
-    refute retweeted_activity.local
-    refute String.contains?(retweeted_object.data["content"], "Test account")
-  end
-
-  test "handle incoming favorites - GS, websub" do
-    capture_log(fn ->
-      incoming = File.read!("test/fixtures/favorite.xml")
-      {:ok, [[activity, favorited_activity]]} = OStatus.handle_incoming(incoming)
-
-      assert activity.data["type"] == "Like"
-      assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
-      assert activity.data["object"] == favorited_activity.data["object"]
-
-      assert activity.data["id"] ==
-               "tag:social.heldscal.la,2017-05-05:fave:23211:comment:2061643:2017-05-05T09:12:50+00:00"
-
-      refute activity.local
-      assert favorited_activity.data["type"] == "Create"
-      assert favorited_activity.data["actor"] == "https://shitposter.club/user/1"
-
-      assert favorited_activity.data["object"] ==
-               "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
-
-      refute favorited_activity.local
-    end)
-  end
-
-  test "handle conversation references" do
-    incoming = File.read!("test/fixtures/mastodon_conversation.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-
-    assert activity.data["context"] ==
-             "tag:mastodon.social,2017-08-28:objectId=7876885:objectType=Conversation"
-  end
-
-  test "handle incoming favorites with locally available object - GS, websub" do
-    note_activity = insert(:note_activity)
-    object = Object.normalize(note_activity)
-
-    incoming =
-      File.read!("test/fixtures/favorite_with_local_note.xml")
-      |> String.replace("localid", object.data["id"])
-
-    {:ok, [[activity, favorited_activity]]} = OStatus.handle_incoming(incoming)
-
-    assert activity.data["type"] == "Like"
-    assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
-    assert activity.data["object"] == object.data["id"]
-    refute activity.local
-    assert note_activity.id == favorited_activity.id
-    assert favorited_activity.local
-  end
-
-  test_with_mock "handle incoming replies, fetching replied-to activities if we don't have them",
-                 OStatus,
-                 [:passthrough],
-                 [] do
-    incoming = File.read!("test/fixtures/incoming_note_activity_answer.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity, false)
-
-    assert activity.data["type"] == "Create"
-    assert object.data["type"] == "Note"
-
-    assert object.data["inReplyTo"] ==
-             "http://pleroma.example.org:4000/objects/55bce8fc-b423-46b1-af71-3759ab4670bc"
-
-    assert "http://pleroma.example.org:4000/users/lain5" in activity.data["to"]
-
-    assert object.data["id"] == "tag:gs.example.org:4040,2017-04-25:noticeId=55:objectType=note"
-
-    assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
-
-    assert called(OStatus.fetch_activity_from_url(object.data["inReplyTo"], :_))
-  end
-
-  test_with_mock "handle incoming replies, not fetching replied-to activities beyond max_replies_depth",
-                 OStatus,
-                 [:passthrough],
-                 [] do
-    incoming = File.read!("test/fixtures/incoming_note_activity_answer.xml")
-
-    with_mock Pleroma.Web.Federator,
-      allowed_incoming_reply_depth?: fn _ -> false end do
-      {:ok, [activity]} = OStatus.handle_incoming(incoming)
-      object = Object.normalize(activity, false)
-
-      refute called(OStatus.fetch_activity_from_url(object.data["inReplyTo"], :_))
-    end
-  end
-
-  test "handle incoming follows" do
-    incoming = File.read!("test/fixtures/follow.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    assert activity.data["type"] == "Follow"
-
-    assert activity.data["id"] ==
-             "tag:social.heldscal.la,2017-05-07:subscription:23211:person:44803:2017-05-07T09:54:48+00:00"
-
-    assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
-    assert activity.data["object"] == "https://pawoo.net/users/pekorino"
-    refute activity.local
-
-    follower = User.get_cached_by_ap_id(activity.data["actor"])
-    followed = User.get_cached_by_ap_id(activity.data["object"])
-
-    assert User.following?(follower, followed)
-  end
-
-  test "refuse following over OStatus if the followed's account is locked" do
-    incoming = File.read!("test/fixtures/follow.xml")
-    _user = insert(:user, info: %{locked: true}, ap_id: "https://pawoo.net/users/pekorino")
-
-    {:ok, [{:error, "It's not possible to follow locked accounts over OStatus"}]} =
-      OStatus.handle_incoming(incoming)
-  end
-
-  test "handle incoming unfollows with existing follow" do
-    incoming_follow = File.read!("test/fixtures/follow.xml")
-    {:ok, [_activity]} = OStatus.handle_incoming(incoming_follow)
-
-    incoming = File.read!("test/fixtures/unfollow.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-
-    assert activity.data["type"] == "Undo"
-
-    assert activity.data["id"] ==
-             "undo:tag:social.heldscal.la,2017-05-07:subscription:23211:person:44803:2017-05-07T09:54:48+00:00"
-
-    assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
-    embedded_object = activity.data["object"]
-    assert is_map(embedded_object)
-    assert embedded_object["type"] == "Follow"
-    assert embedded_object["object"] == "https://pawoo.net/users/pekorino"
-    refute activity.local
-
-    follower = User.get_cached_by_ap_id(activity.data["actor"])
-    followed = User.get_cached_by_ap_id(embedded_object["object"])
-
-    refute User.following?(follower, followed)
-  end
-
-  test "it clears `unreachable` federation status of the sender" do
-    incoming_reaction_xml = File.read!("test/fixtures/share-gs.xml")
-    doc = XML.parse_document(incoming_reaction_xml)
-    actor_uri = XML.string_from_xpath("//author/uri[1]", doc)
-    reacted_to_author_uri = XML.string_from_xpath("//author/uri[2]", doc)
-
-    Instances.set_consistently_unreachable(actor_uri)
-    Instances.set_consistently_unreachable(reacted_to_author_uri)
-    refute Instances.reachable?(actor_uri)
-    refute Instances.reachable?(reacted_to_author_uri)
-
-    {:ok, _} = OStatus.handle_incoming(incoming_reaction_xml)
-    assert Instances.reachable?(actor_uri)
-    refute Instances.reachable?(reacted_to_author_uri)
-  end
-
-  describe "new remote user creation" do
-    test "returns local users" do
-      local_user = insert(:user)
-      {:ok, user} = OStatus.find_or_make_user(local_user.ap_id)
-
-      assert user == local_user
-    end
-
-    test "tries to use the information in poco fields" do
-      uri = "https://social.heldscal.la/user/23211"
-
-      {:ok, user} = OStatus.find_or_make_user(uri)
-
-      user = User.get_cached_by_id(user.id)
-      assert user.name == "Constance Variable"
-      assert user.nickname == "lambadalambda@social.heldscal.la"
-      assert user.local == false
-      assert user.info.uri == uri
-      assert user.ap_id == uri
-      assert user.bio == "Call me Deacon Blues."
-      assert user.avatar["type"] == "Image"
-
-      {:ok, user_again} = OStatus.find_or_make_user(uri)
-
-      assert user == user_again
-    end
-
-    test "find_or_make_user sets all the nessary input fields" do
-      uri = "https://social.heldscal.la/user/23211"
-      {:ok, user} = OStatus.find_or_make_user(uri)
-
-      assert user.info ==
-               %User.Info{
-                 id: user.info.id,
-                 ap_enabled: false,
-                 background: %{},
-                 banner: %{},
-                 blocks: [],
-                 deactivated: false,
-                 default_scope: "public",
-                 domain_blocks: [],
-                 follower_count: 0,
-                 is_admin: false,
-                 is_moderator: false,
-                 keys: nil,
-                 locked: false,
-                 no_rich_text: false,
-                 note_count: 0,
-                 settings: nil,
-                 source_data: %{},
-                 hub: "https://social.heldscal.la/main/push/hub",
-                 magic_key:
-                   "RSA.uzg6r1peZU0vXGADWxGJ0PE34WvmhjUmydbX5YYdOiXfODVLwCMi1umGoqUDm-mRu4vNEdFBVJU1CpFA7dKzWgIsqsa501i2XqElmEveXRLvNRWFB6nG03Q5OUY2as8eE54BJm0p20GkMfIJGwP6TSFb-ICp3QjzbatuSPJ6xCE=.AQAB",
-                 salmon: "https://social.heldscal.la/main/salmon/user/23211",
-                 topic: "https://social.heldscal.la/api/statuses/user_timeline/23211.atom",
-                 uri: "https://social.heldscal.la/user/23211"
-               }
-    end
-
-    test "find_make_or_update_actor takes an author element and returns an updated user" do
-      uri = "https://social.heldscal.la/user/23211"
-
-      {:ok, user} = OStatus.find_or_make_user(uri)
-      old_name = user.name
-      old_bio = user.bio
-      change = Ecto.Changeset.change(user, %{avatar: nil, bio: nil, name: nil})
-
-      {:ok, user} = Repo.update(change)
-      refute user.avatar
-
-      doc = XML.parse_document(File.read!("test/fixtures/23211.atom"))
-      [author] = :xmerl_xpath.string('//author[1]', doc)
-      {:ok, user} = OStatus.find_make_or_update_actor(author)
-      assert user.avatar["type"] == "Image"
-      assert user.name == old_name
-      assert user.bio == old_bio
-
-      {:ok, user_again} = OStatus.find_make_or_update_actor(author)
-      assert user_again == user
-    end
-
-    test "find_or_make_user disallows protocol downgrade" do
-      user = insert(:user, %{local: true})
-      {:ok, user} = OStatus.find_or_make_user(user.ap_id)
-
-      assert User.ap_enabled?(user)
-
-      user =
-        insert(:user, %{
-          ap_id: "https://social.heldscal.la/user/23211",
-          info: %{ap_enabled: true},
-          local: false
-        })
-
-      assert User.ap_enabled?(user)
-
-      {:ok, user} = OStatus.find_or_make_user(user.ap_id)
-      assert User.ap_enabled?(user)
-    end
-
-    test "find_make_or_update_actor disallows protocol downgrade" do
-      user = insert(:user, %{local: true})
-      {:ok, user} = OStatus.find_or_make_user(user.ap_id)
-
-      assert User.ap_enabled?(user)
-
-      user =
-        insert(:user, %{
-          ap_id: "https://social.heldscal.la/user/23211",
-          info: %{ap_enabled: true},
-          local: false
-        })
-
-      assert User.ap_enabled?(user)
-
-      {:ok, user} = OStatus.find_or_make_user(user.ap_id)
-      assert User.ap_enabled?(user)
-
-      doc = XML.parse_document(File.read!("test/fixtures/23211.atom"))
-      [author] = :xmerl_xpath.string('//author[1]', doc)
-      {:error, :invalid_protocol} = OStatus.find_make_or_update_actor(author)
-    end
-  end
-
-  describe "gathering user info from a user id" do
-    test "it returns user info in a hash" do
-      user = "shp@social.heldscal.la"
-
-      # TODO: make test local
-      {:ok, data} = OStatus.gather_user_info(user)
-
-      expected = %{
-        "hub" => "https://social.heldscal.la/main/push/hub",
-        "magic_key" =>
-          "RSA.wQ3i9UA0qmAxZ0WTIp4a-waZn_17Ez1pEEmqmqoooRsG1_BvpmOvLN0G2tEcWWxl2KOtdQMCiPptmQObeZeuj48mdsDZ4ArQinexY2hCCTcbV8Xpswpkb8K05RcKipdg07pnI7tAgQ0VWSZDImncL6YUGlG5YN8b5TjGOwk2VG8=.AQAB",
-        "name" => "shp",
-        "nickname" => "shp",
-        "salmon" => "https://social.heldscal.la/main/salmon/user/29191",
-        "subject" => "acct:shp@social.heldscal.la",
-        "topic" => "https://social.heldscal.la/api/statuses/user_timeline/29191.atom",
-        "uri" => "https://social.heldscal.la/user/29191",
-        "host" => "social.heldscal.la",
-        "fqn" => user,
-        "bio" => "cofe",
-        "avatar" => %{
-          "type" => "Image",
-          "url" => [
-            %{
-              "href" => "https://social.heldscal.la/avatar/29191-original-20170421154949.jpeg",
-              "mediaType" => "image/jpeg",
-              "type" => "Link"
-            }
-          ]
-        },
-        "subscribe_address" => "https://social.heldscal.la/main/ostatussub?profile={uri}",
-        "ap_id" => nil
-      }
-
-      assert data == expected
-    end
-
-    test "it works with the uri" do
-      user = "https://social.heldscal.la/user/29191"
-
-      # TODO: make test local
-      {:ok, data} = OStatus.gather_user_info(user)
-
-      expected = %{
-        "hub" => "https://social.heldscal.la/main/push/hub",
-        "magic_key" =>
-          "RSA.wQ3i9UA0qmAxZ0WTIp4a-waZn_17Ez1pEEmqmqoooRsG1_BvpmOvLN0G2tEcWWxl2KOtdQMCiPptmQObeZeuj48mdsDZ4ArQinexY2hCCTcbV8Xpswpkb8K05RcKipdg07pnI7tAgQ0VWSZDImncL6YUGlG5YN8b5TjGOwk2VG8=.AQAB",
-        "name" => "shp",
-        "nickname" => "shp",
-        "salmon" => "https://social.heldscal.la/main/salmon/user/29191",
-        "subject" => "https://social.heldscal.la/user/29191",
-        "topic" => "https://social.heldscal.la/api/statuses/user_timeline/29191.atom",
-        "uri" => "https://social.heldscal.la/user/29191",
-        "host" => "social.heldscal.la",
-        "fqn" => user,
-        "bio" => "cofe",
-        "avatar" => %{
-          "type" => "Image",
-          "url" => [
-            %{
-              "href" => "https://social.heldscal.la/avatar/29191-original-20170421154949.jpeg",
-              "mediaType" => "image/jpeg",
-              "type" => "Link"
-            }
-          ]
-        },
-        "subscribe_address" => "https://social.heldscal.la/main/ostatussub?profile={uri}",
-        "ap_id" => nil
-      }
-
-      assert data == expected
-    end
-  end
-
-  describe "fetching a status by it's HTML url" do
-    test "it builds a missing status from an html url" do
-      capture_log(fn ->
-        url = "https://shitposter.club/notice/2827873"
-        {:ok, [activity]} = OStatus.fetch_activity_from_url(url)
-
-        assert activity.data["actor"] == "https://shitposter.club/user/1"
-
-        assert activity.data["object"] ==
-                 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
-      end)
-    end
-
-    test "it works for atom notes, too" do
-      url = "https://social.sakamoto.gq/objects/0ccc1a2c-66b0-4305-b23a-7f7f2b040056"
-      {:ok, [activity]} = OStatus.fetch_activity_from_url(url)
-      assert activity.data["actor"] == "https://social.sakamoto.gq/users/eal"
-      assert activity.data["object"] == url
-    end
-  end
-
-  test "it doesn't add nil in the to field" do
-    incoming = File.read!("test/fixtures/nil_mention_entry.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-
-    assert activity.data["to"] == [
-             "http://localhost:4001/users/atarifrosch@social.stopwatchingus-heidelberg.de/followers",
-             "https://www.w3.org/ns/activitystreams#Public"
-           ]
-  end
-
-  describe "is_representable?" do
-    test "Note objects are representable" do
-      note_activity = insert(:note_activity)
-
-      assert OStatus.is_representable?(note_activity)
-    end
-
-    test "Article objects are not representable" do
-      note_activity = insert(:note_activity)
-      note_object = Object.normalize(note_activity)
-
-      note_data =
-        note_object.data
-        |> Map.put("type", "Article")
-
-      Cachex.clear(:object_cache)
-
-      cs = Object.change(note_object, %{data: note_data})
-      {:ok, _article_object} = Repo.update(cs)
-
-      # the underlying object is now an Article instead of a note, so this should fail
-      refute OStatus.is_representable?(note_activity)
-    end
-  end
-
-  describe "make_user/2" do
-    test "creates new user" do
-      {:ok, user} = OStatus.make_user("https://social.heldscal.la/user/23211")
-
-      created_user =
-        User
-        |> Repo.get_by(ap_id: "https://social.heldscal.la/user/23211")
-        |> Map.put(:last_digest_emailed_at, nil)
-
-      assert user.info
-      assert user == created_user
-    end
-  end
-end
diff --git a/test/web/ostatus/user_representer_test.exs b/test/web/ostatus/user_representer_test.exs
deleted file mode 100644 (file)
index e3863d2..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.UserRepresenterTest do
-  use Pleroma.DataCase
-  alias Pleroma.Web.OStatus.UserRepresenter
-
-  import Pleroma.Factory
-  alias Pleroma.User
-
-  test "returns a user with id, uri, name and link" do
-    user = insert(:user, %{nickname: "レイン"})
-    tuple = UserRepresenter.to_simple_form(user)
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> to_string
-
-    expected = """
-    <id>#{user.ap_id}</id>
-    <activity:object>http://activitystrea.ms/schema/1.0/person</activity:object>
-    <uri>#{user.ap_id}</uri>
-    <poco:preferredUsername>#{user.nickname}</poco:preferredUsername>
-    <poco:displayName>#{user.name}</poco:displayName>
-    <poco:note>#{user.bio}</poco:note>
-    <summary>#{user.bio}</summary>
-    <name>#{user.nickname}</name>
-    <link rel="avatar" href="#{User.avatar_url(user)}" />
-    <link rel="header" href="#{User.banner_url(user)}" />
-    <ap_enabled>true</ap_enabled>
-    """
-
-    assert clean(res) == clean(expected)
-  end
-
-  defp clean(string) do
-    String.replace(string, ~r/\s/, "")
-  end
-end
index 3b4665afdd7737001761c5b94b8b6c58827951d5..c809f510ff999881f7f6703346698a470f54e1ee 100644 (file)
@@ -6,7 +6,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
   use Pleroma.Web.ConnCase
 
   alias Pleroma.Config
-  alias Pleroma.Repo
   alias Pleroma.Tests.ObanHelpers
   alias Pleroma.User
   alias Pleroma.Web.CommonAPI
@@ -20,10 +19,10 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
     setup do
       {:ok, user} =
         insert(:user)
-        |> User.change_info(&User.Info.confirmation_changeset(&1, need_confirmation: true))
-        |> Repo.update()
+        |> User.confirmation_changeset(need_confirmation: true)
+        |> User.update_and_set_cache()
 
-      assert user.info.confirmation_pending
+      assert user.confirmation_pending
 
       [user: user]
     end
@@ -105,7 +104,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
         |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
 
       user = refresh_record(user)
-      assert user.info.banner["type"] == "Image"
+      assert user.banner["type"] == "Image"
 
       assert %{"url" => _} = json_response(conn, 200)
     end
@@ -119,7 +118,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
         |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
 
       user = refresh_record(user)
-      assert user.info.banner == %{}
+      assert user.banner == %{}
 
       assert %{"url" => nil} = json_response(conn, 200)
     end
@@ -135,7 +134,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
         |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
 
       user = refresh_record(user)
-      assert user.info.background["type"] == "Image"
+      assert user.background["type"] == "Image"
       assert %{"url" => _} = json_response(conn, 200)
     end
 
@@ -148,14 +147,14 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
         |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
 
       user = refresh_record(user)
-      assert user.info.background == %{}
+      assert user.background == %{}
       assert %{"url" => nil} = json_response(conn, 200)
     end
   end
 
   describe "getting favorites timeline of specified user" do
     setup do
-      [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
+      [current_user, user] = insert_pair(:user, hide_favorites: false)
       [current_user: current_user, user: user]
     end
 
@@ -319,7 +318,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
       conn: conn,
       current_user: current_user
     } do
-      user = insert(:user, %{info: %{hide_favorites: true}})
+      user = insert(:user, hide_favorites: true)
       activity = insert(:note_activity)
       CommonAPI.favorite(activity.id, user)
 
@@ -341,7 +340,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
         |> assign(:user, current_user)
         |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
 
-      assert user.info.hide_favorites
+      assert user.hide_favorites
       assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
     end
   end
index 5f74460e8e40e46c5778d9509c82456e4416df36..3d3becefdb56436cb6e42036500739378f3ec365 100644 (file)
@@ -38,7 +38,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
   end
 
   test "listing remote packs" do
-    admin = insert(:user, info: %{is_admin: true})
+    admin = insert(:user, is_admin: true)
     conn = build_conn() |> assign(:user, admin)
 
     resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200)
@@ -121,7 +121,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
         text(File.read!("#{@emoji_dir_path}/test_pack_nonshared/nonshared.zip"))
     end)
 
-    admin = insert(:user, info: %{is_admin: true})
+    admin = insert(:user, is_admin: true)
 
     conn = build_conn() |> assign(:user, admin)
 
@@ -206,7 +206,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
       end)
 
       {:ok,
-       admin: insert(:user, info: %{is_admin: true}),
+       admin: insert(:user, is_admin: true),
        pack_file: pack_file,
        new_data: %{
          "license" => "Test license changed",
@@ -303,7 +303,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
       File.rm_rf!("#{@emoji_dir_path}/test_pack/dir_2")
     end)
 
-    admin = insert(:user, info: %{is_admin: true})
+    admin = insert(:user, is_admin: true)
 
     conn = build_conn()
 
@@ -391,7 +391,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
       File.rm_rf!("#{@emoji_dir_path}/test_created")
     end)
 
-    admin = insert(:user, info: %{is_admin: true})
+    admin = insert(:user, is_admin: true)
 
     conn = build_conn() |> assign(:user, admin)
 
@@ -431,7 +431,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
 
     refute Map.has_key?(resp, "test_pack_for_import")
 
-    admin = insert(:user, info: %{is_admin: true})
+    admin = insert(:user, is_admin: true)
 
     assert conn
            |> assign(:user, admin)
index 8a6528cbb1b5b874a14240cbfa948d115fcb175b..0c83edb56173785cf70a8f640af146083fb1d9f2 100644 (file)
@@ -95,6 +95,33 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
     assert other_user in participation.recipients
   end
 
+  test "POST /api/v1/pleroma/conversations/read", %{conn: conn} do
+    user = insert(:user)
+    other_user = insert(:user)
+
+    {:ok, _activity} =
+      CommonAPI.post(user, %{"status" => "Hi @#{other_user.nickname}", "visibility" => "direct"})
+
+    {:ok, _activity} =
+      CommonAPI.post(user, %{"status" => "Hi @#{other_user.nickname}", "visibility" => "direct"})
+
+    [participation2, participation1] = Participation.for_user(other_user)
+    assert Participation.get(participation2.id).read == false
+    assert Participation.get(participation1.id).read == false
+    assert User.get_cached_by_id(other_user.id).unread_conversation_count == 2
+
+    [%{"unread" => false}, %{"unread" => false}] =
+      conn
+      |> assign(:user, other_user)
+      |> post("/api/v1/pleroma/conversations/read", %{})
+      |> json_response(200)
+
+    [participation2, participation1] = Participation.for_user(other_user)
+    assert Participation.get(participation2.id).read == true
+    assert Participation.get(participation1.id).read == true
+    assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0
+  end
+
   describe "POST /api/v1/pleroma/notifications/read" do
     test "it marks a single notification as read", %{conn: conn} do
       user1 = insert(:user)
index 2f6ce4bd206c862865c006f631ea886eb16e7884..9b554601d9c146079ccb1809a49ba37aef522bbd 100644 (file)
@@ -84,7 +84,7 @@ defmodule Pleroma.Web.Push.ImplTest do
            ) == :error
   end
 
-  test "delete subsciption if restult send message between 400..500" do
+  test "delete subscription if result send message between 400..500" do
     subscription = insert(:push_subscription)
 
     assert Impl.push_message(
@@ -97,7 +97,7 @@ defmodule Pleroma.Web.Push.ImplTest do
     refute Pleroma.Repo.get(Subscription, subscription.id)
   end
 
-  test "renders body for create activity" do
+  test "renders title and body for create activity" do
     user = insert(:user, nickname: "Bob")
 
     {:ok, activity} =
@@ -116,18 +116,24 @@ defmodule Pleroma.Web.Push.ImplTest do
              object
            ) ==
              "@Bob: Lorem ipsum dolor sit amet, consectetur  adipiscing elit. Fusce sagittis fini..."
+
+    assert Impl.format_title(%{activity: activity}) ==
+             "New Mention"
   end
 
-  test "renders body for follow activity" do
+  test "renders title and body for follow activity" do
     user = insert(:user, nickname: "Bob")
     other_user = insert(:user)
     {:ok, _, _, activity} = CommonAPI.follow(user, other_user)
     object = Object.normalize(activity)
 
     assert Impl.format_body(%{activity: activity}, user, object) == "@Bob has followed you"
+
+    assert Impl.format_title(%{activity: activity}) ==
+             "New Follower"
   end
 
-  test "renders body for announce activity" do
+  test "renders title and body for announce activity" do
     user = insert(:user)
 
     {:ok, activity} =
@@ -141,9 +147,12 @@ defmodule Pleroma.Web.Push.ImplTest do
 
     assert Impl.format_body(%{activity: announce_activity}, user, object) ==
              "@#{user.nickname} repeated: Lorem ipsum dolor sit amet, consectetur  adipiscing elit. Fusce sagittis fini..."
+
+    assert Impl.format_title(%{activity: announce_activity}) ==
+             "New Repeat"
   end
 
-  test "renders body for like activity" do
+  test "renders title and body for like activity" do
     user = insert(:user, nickname: "Bob")
 
     {:ok, activity} =
@@ -156,5 +165,21 @@ defmodule Pleroma.Web.Push.ImplTest do
     object = Object.normalize(activity)
 
     assert Impl.format_body(%{activity: activity}, user, object) == "@Bob has favorited your post"
+
+    assert Impl.format_title(%{activity: activity}) ==
+             "New Favorite"
+  end
+
+  test "renders title for create activity with direct visibility" do
+    user = insert(:user, nickname: "Bob")
+
+    {:ok, activity} =
+      CommonAPI.post(user, %{
+        "visibility" => "direct",
+        "status" => "This is just between you and me, pal"
+      })
+
+    assert Impl.format_title(%{activity: activity}) ==
+             "New Direct Message"
   end
 end
diff --git a/test/web/salmon/salmon_test.exs b/test/web/salmon/salmon_test.exs
deleted file mode 100644 (file)
index 153ec41..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Salmon.SalmonTest do
-  use Pleroma.DataCase
-  alias Pleroma.Activity
-  alias Pleroma.Keys
-  alias Pleroma.Repo
-  alias Pleroma.User
-  alias Pleroma.Web.Federator.Publisher
-  alias Pleroma.Web.Salmon
-  import Mock
-  import Pleroma.Factory
-
-  @magickey "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwQhh-1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAB"
-
-  @wrong_magickey "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwQhh-1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAA"
-
-  @magickey_friendica "RSA.AMwa8FUs2fWEjX0xN7yRQgegQffhBpuKNC6fa5VNSVorFjGZhRrlPMn7TQOeihlc9lBz2OsHlIedbYn2uJ7yCs0.AQAB"
-
-  setup_all do
-    Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
-    :ok
-  end
-
-  test "decodes a salmon" do
-    {:ok, salmon} = File.read("test/fixtures/salmon.xml")
-    {:ok, doc} = Salmon.decode_and_validate(@magickey, salmon)
-    assert Regex.match?(~r/xml/, doc)
-  end
-
-  test "errors on wrong magic key" do
-    {:ok, salmon} = File.read("test/fixtures/salmon.xml")
-    assert Salmon.decode_and_validate(@wrong_magickey, salmon) == :error
-  end
-
-  test "it encodes a magic key from a public key" do
-    key = Salmon.decode_key(@magickey)
-    magic_key = Salmon.encode_key(key)
-
-    assert @magickey == magic_key
-  end
-
-  test "it decodes a friendica public key" do
-    _key = Salmon.decode_key(@magickey_friendica)
-  end
-
-  test "encodes an xml payload with a private key" do
-    doc = File.read!("test/fixtures/incoming_note_activity.xml")
-    pem = File.read!("test/fixtures/private_key.pem")
-    {:ok, private, public} = Keys.keys_from_pem(pem)
-
-    # Let's try a roundtrip.
-    {:ok, salmon} = Salmon.encode(private, doc)
-    {:ok, decoded_doc} = Salmon.decode_and_validate(Salmon.encode_key(public), salmon)
-
-    assert doc == decoded_doc
-  end
-
-  test "it gets a magic key" do
-    salmon = File.read!("test/fixtures/salmon2.xml")
-    {:ok, key} = Salmon.fetch_magic_key(salmon)
-
-    assert key ==
-             "RSA.uzg6r1peZU0vXGADWxGJ0PE34WvmhjUmydbX5YYdOiXfODVLwCMi1umGoqUDm-mRu4vNEdFBVJU1CpFA7dKzWgIsqsa501i2XqElmEveXRLvNRWFB6nG03Q5OUY2as8eE54BJm0p20GkMfIJGwP6TSFb-ICp3QjzbatuSPJ6xCE=.AQAB"
-  end
-
-  test_with_mock "it pushes an activity to remote accounts it's addressed to",
-                 Publisher,
-                 [:passthrough],
-                 [] do
-    user_data = %{
-      info: %{
-        salmon: "http://test-example.org/salmon"
-      },
-      local: false
-    }
-
-    mentioned_user = insert(:user, user_data)
-    note = insert(:note)
-
-    activity_data = %{
-      "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
-      "type" => "Create",
-      "actor" => note.data["actor"],
-      "to" => note.data["to"] ++ [mentioned_user.ap_id],
-      "object" => note.data,
-      "published_at" => DateTime.utc_now() |> DateTime.to_iso8601(),
-      "context" => note.data["context"]
-    }
-
-    {:ok, activity} = Repo.insert(%Activity{data: activity_data, recipients: activity_data["to"]})
-    user = User.get_cached_by_ap_id(activity.data["actor"])
-    {:ok, user} = User.ensure_keys_present(user)
-
-    Salmon.publish(user, activity)
-
-    assert called(Publisher.enqueue_one(Salmon, %{recipient_id: mentioned_user.id}))
-  end
-end
index d33eb1e4228e1a3a911b49e604626b745b77d170..400f3287d254bcc4ba92eceaa8493d5a237ee3e0 100644 (file)
@@ -213,7 +213,7 @@ defmodule Pleroma.Web.StreamerTest do
     test "it sends message if recipients invalid and thread containment is enabled but user's thread containment is disabled" do
       Pleroma.Config.put([:instance, :skip_thread_containment], false)
       author = insert(:user)
-      user = insert(:user, following: [author.ap_id], info: %{skip_thread_containment: true})
+      user = insert(:user, following: [author.ap_id], skip_thread_containment: true)
 
       activity =
         insert(:note_activity,
index dc6d4e3e32b43cf8a143717a5c27c424fd85af49..840c84a0587cd34f49dc122a87b55a30d5cd09a6 100644 (file)
@@ -59,7 +59,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do
     end
 
     test "it sets password_reset_pending to false", %{conn: conn} do
-      user = insert(:user, info: %{password_reset_pending: true})
+      user = insert(:user, password_reset_pending: true)
 
       {:ok, token} = PasswordResetToken.create_token(user)
       {:ok, _access_token} = Token.create_token(insert(:oauth_app), user, %{})
@@ -75,7 +75,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do
       |> post("/api/pleroma/password_reset", %{data: params})
       |> html_response(:ok)
 
-      assert User.get_by_id(user.id).info.password_reset_pending == false
+      assert User.get_by_id(user.id).password_reset_pending == false
     end
   end
 end
index d1d61d11a53e2d2bb5f4374d578dc78a84048807..85a9be3e087b6dd32a24ef96c55178f7f29f133c 100644 (file)
@@ -71,7 +71,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
     {:ok, user} = TwitterAPI.register_user(data)
     ObanHelpers.perform_all()
 
-    assert user.info.confirmation_pending
+    assert user.confirmation_pending
 
     email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
 
index 9d4cb70f0677a2498b90d42fb369acb4687da78f..246da4da414872930cac7249106fbf5c855e0dfe 100644 (file)
@@ -164,7 +164,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
                "follows" => true,
                "non_follows" => true,
                "non_followers" => true
-             } == user.info.notification_settings
+             } == user.notification_settings
     end
   end
 
@@ -370,7 +370,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
     end
 
     test "returns error when user is deactivated", %{conn: conn} do
-      user = insert(:user, info: %{deactivated: true})
+      user = insert(:user, deactivated: true)
       user2 = insert(:user)
 
       response =
@@ -568,7 +568,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
 
       user = User.get_cached_by_id(user.id)
 
-      assert user.info.deactivated == true
+      assert user.deactivated == true
     end
 
     test "it returns returns when password invalid", %{conn: conn} do
@@ -583,7 +583,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
       assert response == %{"error" => "Invalid password."}
       user = User.get_cached_by_id(user.id)
 
-      refute user.info.deactivated
+      refute user.deactivated
     end
   end
 
index 696c1bd7067c7e9cd2b29deff185ac83e5fc8f18..5aa8c73cf8d01c8036eba217ad40ad6676cb3423 100644 (file)
@@ -45,19 +45,6 @@ defmodule Pleroma.Web.WebFingerTest do
       assert {:error, %Jason.DecodeError{}} = WebFinger.finger(user)
     end
 
-    test "returns the info for an OStatus user" do
-      user = "shp@social.heldscal.la"
-
-      {:ok, data} = WebFinger.finger(user)
-
-      assert data["magic_key"] ==
-               "RSA.wQ3i9UA0qmAxZ0WTIp4a-waZn_17Ez1pEEmqmqoooRsG1_BvpmOvLN0G2tEcWWxl2KOtdQMCiPptmQObeZeuj48mdsDZ4ArQinexY2hCCTcbV8Xpswpkb8K05RcKipdg07pnI7tAgQ0VWSZDImncL6YUGlG5YN8b5TjGOwk2VG8=.AQAB"
-
-      assert data["topic"] == "https://social.heldscal.la/api/statuses/user_timeline/29191.atom"
-      assert data["subject"] == "acct:shp@social.heldscal.la"
-      assert data["salmon"] == "https://social.heldscal.la/main/salmon/user/29191"
-    end
-
     test "returns the ActivityPub actor URI for an ActivityPub user" do
       user = "framasoft@framatube.org"
 
@@ -72,20 +59,6 @@ defmodule Pleroma.Web.WebFingerTest do
       assert data["ap_id"] == "https://gerzilla.de/channel/kaniini"
     end
 
-    test "returns the correctly for json ostatus users" do
-      user = "winterdienst@gnusocial.de"
-
-      {:ok, data} = WebFinger.finger(user)
-
-      assert data["magic_key"] ==
-               "RSA.qfYaxztz7ZELrE4v5WpJrPM99SKI3iv9Y3Tw6nfLGk-4CRljNYqV8IYX2FXjeucC_DKhPNnlF6fXyASpcSmA_qupX9WC66eVhFhZ5OuyBOeLvJ1C4x7Hi7Di8MNBxY3VdQuQR0tTaS_YAZCwASKp7H6XEid3EJpGt0EQZoNzRd8=.AQAB"
-
-      assert data["topic"] == "https://gnusocial.de/api/statuses/user_timeline/249296.atom"
-      assert data["subject"] == "acct:winterdienst@gnusocial.de"
-      assert data["salmon"] == "https://gnusocial.de/main/salmon/user/249296"
-      assert data["subscribe_address"] == "https://gnusocial.de/main/ostatussub?profile={uri}"
-    end
-
     test "it work for AP-only user" do
       user = "kpherox@mstdn.jp"
 
diff --git a/test/web/websub/websub_controller_test.exs b/test/web/websub/websub_controller_test.exs
deleted file mode 100644 (file)
index f6d002b..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Websub.WebsubControllerTest do
-  use Pleroma.Web.ConnCase
-  import Pleroma.Factory
-  alias Pleroma.Repo
-  alias Pleroma.Web.Websub
-  alias Pleroma.Web.Websub.WebsubClientSubscription
-
-  clear_config_all([:instance, :federating]) do
-    Pleroma.Config.put([:instance, :federating], true)
-  end
-
-  test "websub subscription request", %{conn: conn} do
-    user = insert(:user)
-
-    path = Pleroma.Web.OStatus.pubsub_path(user)
-
-    data = %{
-      "hub.callback": "http://example.org/sub",
-      "hub.mode": "subscribe",
-      "hub.topic": Pleroma.Web.OStatus.feed_path(user),
-      "hub.secret": "a random secret",
-      "hub.lease_seconds": "100"
-    }
-
-    conn =
-      conn
-      |> post(path, data)
-
-    assert response(conn, 202) == "Accepted"
-  end
-
-  test "websub subscription confirmation", %{conn: conn} do
-    websub = insert(:websub_client_subscription)
-
-    params = %{
-      "hub.mode" => "subscribe",
-      "hub.topic" => websub.topic,
-      "hub.challenge" => "some challenge",
-      "hub.lease_seconds" => "100"
-    }
-
-    conn =
-      conn
-      |> get("/push/subscriptions/#{websub.id}", params)
-
-    websub = Repo.get(WebsubClientSubscription, websub.id)
-
-    assert response(conn, 200) == "some challenge"
-    assert websub.state == "accepted"
-    assert_in_delta NaiveDateTime.diff(websub.valid_until, NaiveDateTime.utc_now()), 100, 5
-  end
-
-  describe "websub_incoming" do
-    test "accepts incoming feed updates", %{conn: conn} do
-      websub = insert(:websub_client_subscription)
-      doc = "some stuff"
-      signature = Websub.sign(websub.secret, doc)
-
-      conn =
-        conn
-        |> put_req_header("x-hub-signature", "sha1=" <> signature)
-        |> put_req_header("content-type", "application/atom+xml")
-        |> post("/push/subscriptions/#{websub.id}", doc)
-
-      assert response(conn, 200) == "OK"
-    end
-
-    test "rejects incoming feed updates with the wrong signature", %{conn: conn} do
-      websub = insert(:websub_client_subscription)
-      doc = "some stuff"
-      signature = Websub.sign("wrong secret", doc)
-
-      conn =
-        conn
-        |> put_req_header("x-hub-signature", "sha1=" <> signature)
-        |> put_req_header("content-type", "application/atom+xml")
-        |> post("/push/subscriptions/#{websub.id}", doc)
-
-      assert response(conn, 500) == "Error"
-    end
-  end
-end
diff --git a/test/web/websub/websub_test.exs b/test/web/websub/websub_test.exs
deleted file mode 100644 (file)
index 46ca545..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.WebsubTest do
-  use Pleroma.DataCase
-  use Oban.Testing, repo: Pleroma.Repo
-
-  alias Pleroma.Tests.ObanHelpers
-  alias Pleroma.Web.Router.Helpers
-  alias Pleroma.Web.Websub
-  alias Pleroma.Web.Websub.WebsubClientSubscription
-  alias Pleroma.Web.Websub.WebsubServerSubscription
-  alias Pleroma.Workers.SubscriberWorker
-
-  import Pleroma.Factory
-  import Tesla.Mock
-
-  setup do
-    mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
-    :ok
-  end
-
-  test "a verification of a request that is accepted" do
-    sub = insert(:websub_subscription)
-    topic = sub.topic
-
-    getter = fn _path, _headers, options ->
-      %{
-        "hub.challenge": challenge,
-        "hub.lease_seconds": seconds,
-        "hub.topic": ^topic,
-        "hub.mode": "subscribe"
-      } = Keyword.get(options, :params)
-
-      assert String.to_integer(seconds) > 0
-
-      {:ok,
-       %Tesla.Env{
-         status: 200,
-         body: challenge
-       }}
-    end
-
-    {:ok, sub} = Websub.verify(sub, getter)
-    assert sub.state == "active"
-  end
-
-  test "a verification of a request that doesn't return 200" do
-    sub = insert(:websub_subscription)
-
-    getter = fn _path, _headers, _options ->
-      {:ok,
-       %Tesla.Env{
-         status: 500,
-         body: ""
-       }}
-    end
-
-    {:error, sub} = Websub.verify(sub, getter)
-    # Keep the current state.
-    assert sub.state == "requested"
-  end
-
-  test "an incoming subscription request" do
-    user = insert(:user)
-
-    data = %{
-      "hub.callback" => "http://example.org/sub",
-      "hub.mode" => "subscribe",
-      "hub.topic" => Pleroma.Web.OStatus.feed_path(user),
-      "hub.secret" => "a random secret",
-      "hub.lease_seconds" => "100"
-    }
-
-    {:ok, subscription} = Websub.incoming_subscription_request(user, data)
-    assert subscription.topic == Pleroma.Web.OStatus.feed_path(user)
-    assert subscription.state == "requested"
-    assert subscription.secret == "a random secret"
-    assert subscription.callback == "http://example.org/sub"
-  end
-
-  test "an incoming subscription request for an existing subscription" do
-    user = insert(:user)
-
-    sub =
-      insert(:websub_subscription, state: "accepted", topic: Pleroma.Web.OStatus.feed_path(user))
-
-    data = %{
-      "hub.callback" => sub.callback,
-      "hub.mode" => "subscribe",
-      "hub.topic" => Pleroma.Web.OStatus.feed_path(user),
-      "hub.secret" => "a random secret",
-      "hub.lease_seconds" => "100"
-    }
-
-    {:ok, subscription} = Websub.incoming_subscription_request(user, data)
-    assert subscription.topic == Pleroma.Web.OStatus.feed_path(user)
-    assert subscription.state == sub.state
-    assert subscription.secret == "a random secret"
-    assert subscription.callback == sub.callback
-    assert length(Repo.all(WebsubServerSubscription)) == 1
-    assert subscription.id == sub.id
-  end
-
-  def accepting_verifier(subscription) do
-    {:ok, %{subscription | state: "accepted"}}
-  end
-
-  test "initiate a subscription for a given user and topic" do
-    subscriber = insert(:user)
-    user = insert(:user, %{info: %Pleroma.User.Info{topic: "some_topic", hub: "some_hub"}})
-
-    {:ok, websub} = Websub.subscribe(subscriber, user, &accepting_verifier/1)
-    assert websub.subscribers == [subscriber.ap_id]
-    assert websub.topic == "some_topic"
-    assert websub.hub == "some_hub"
-    assert is_binary(websub.secret)
-    assert websub.user == user
-    assert websub.state == "accepted"
-  end
-
-  test "discovers the hub and canonical url" do
-    topic = "https://mastodon.social/users/lambadalambda.atom"
-
-    {:ok, discovered} = Websub.gather_feed_data(topic)
-
-    expected = %{
-      "hub" => "https://mastodon.social/api/push",
-      "uri" => "https://mastodon.social/users/lambadalambda",
-      "nickname" => "lambadalambda",
-      "name" => "Critical Value",
-      "host" => "mastodon.social",
-      "bio" => "a cool dude.",
-      "avatar" => %{
-        "type" => "Image",
-        "url" => [
-          %{
-            "href" =>
-              "https://files.mastodon.social/accounts/avatars/000/000/264/original/1429214160519.gif?1492379244",
-            "mediaType" => "image/gif",
-            "type" => "Link"
-          }
-        ]
-      }
-    }
-
-    assert expected == discovered
-  end
-
-  test "calls the hub, requests topic" do
-    hub = "https://social.heldscal.la/main/push/hub"
-    topic = "https://social.heldscal.la/api/statuses/user_timeline/23211.atom"
-    websub = insert(:websub_client_subscription, %{hub: hub, topic: topic})
-
-    poster = fn ^hub, {:form, data}, _headers ->
-      assert Keyword.get(data, :"hub.mode") == "subscribe"
-
-      assert Keyword.get(data, :"hub.callback") ==
-               Helpers.websub_url(
-                 Pleroma.Web.Endpoint,
-                 :websub_subscription_confirmation,
-                 websub.id
-               )
-
-      {:ok, %{status: 202}}
-    end
-
-    task = Task.async(fn -> Websub.request_subscription(websub, poster) end)
-
-    change = Ecto.Changeset.change(websub, %{state: "accepted"})
-    {:ok, _} = Repo.update(change)
-
-    {:ok, websub} = Task.await(task)
-
-    assert websub.state == "accepted"
-  end
-
-  test "rejects the subscription if it can't be accepted" do
-    hub = "https://social.heldscal.la/main/push/hub"
-    topic = "https://social.heldscal.la/api/statuses/user_timeline/23211.atom"
-    websub = insert(:websub_client_subscription, %{hub: hub, topic: topic})
-
-    poster = fn ^hub, {:form, _data}, _headers ->
-      {:ok, %{status: 202}}
-    end
-
-    {:error, websub} = Websub.request_subscription(websub, poster, 1000)
-    assert websub.state == "rejected"
-
-    websub = insert(:websub_client_subscription, %{hub: hub, topic: topic})
-
-    poster = fn ^hub, {:form, _data}, _headers ->
-      {:ok, %{status: 400}}
-    end
-
-    {:error, websub} = Websub.request_subscription(websub, poster, 1000)
-    assert websub.state == "rejected"
-  end
-
-  test "sign a text" do
-    signed = Websub.sign("secret", "text")
-    assert signed == "B8392C23690CCF871F37EC270BE1582DEC57A503" |> String.downcase()
-
-    _signed = Websub.sign("secret", [["て"], ['す']])
-  end
-
-  describe "renewing subscriptions" do
-    test "it renews subscriptions that have less than a day of time left" do
-      day = 60 * 60 * 24
-      now = NaiveDateTime.utc_now()
-
-      still_good =
-        insert(:websub_client_subscription, %{
-          valid_until: NaiveDateTime.add(now, 2 * day),
-          topic: "http://example.org/still_good",
-          hub: "http://example.org/still_good",
-          state: "accepted"
-        })
-
-      needs_refresh =
-        insert(:websub_client_subscription, %{
-          valid_until: NaiveDateTime.add(now, day - 100),
-          topic: "http://example.org/needs_refresh",
-          hub: "http://example.org/needs_refresh",
-          state: "accepted"
-        })
-
-      _refresh = Websub.refresh_subscriptions()
-      ObanHelpers.perform(all_enqueued(worker: SubscriberWorker))
-
-      assert still_good == Repo.get(WebsubClientSubscription, still_good.id)
-      refute needs_refresh == Repo.get(WebsubClientSubscription, needs_refresh.id)
-    end
-  end
-end